mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-01 08:43:30 +02:00
merge
This commit is contained in:
commit
419eaf8709
46 changed files with 654 additions and 296 deletions
|
@ -69,8 +69,8 @@ class AvailableViewTest(MockEppLib):
|
|||
self.assertTrue(check_domain_available("igorville.gov"))
|
||||
# input is lowercased so GSA.GOV should also not be available
|
||||
self.assertFalse(check_domain_available("GSA.gov"))
|
||||
# input is lowercased so IGORVILLE.GOV should also not be available
|
||||
self.assertFalse(check_domain_available("IGORVILLE.gov"))
|
||||
# input is lowercased so IGORVILLE.GOV should also be available
|
||||
self.assertTrue(check_domain_available("IGORVILLE.gov"))
|
||||
|
||||
def test_domain_available_dotgov(self):
|
||||
"""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"))
|
||||
),
|
||||
"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),
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ except ImportError:
|
|||
from django.conf import settings
|
||||
|
||||
from .cert import Cert, Key
|
||||
from .errors import LoginError, RegistryError
|
||||
from .errors import ErrorCode, LoginError, RegistryError
|
||||
from .socket import Socket
|
||||
from .utility.pool import EPPConnectionPool
|
||||
|
||||
|
@ -115,7 +115,7 @@ class EPPLibWrapper:
|
|||
except TransportError as err:
|
||||
message = f"{cmd_type} failed to execute due to a connection error."
|
||||
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:
|
||||
# For linter due to it not liking this line length
|
||||
text = "failed to execute due to a registry login error."
|
||||
|
@ -163,7 +163,8 @@ class EPPLibWrapper:
|
|||
try:
|
||||
return self._send(command)
|
||||
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
|
||||
sleep((counter * 50) / 1000) # sleep 50 ms to 150 ms
|
||||
else: # don't try again
|
||||
|
|
|
@ -4,13 +4,15 @@ from enum import IntEnum
|
|||
class ErrorCode(IntEnum):
|
||||
"""
|
||||
Overview of registry response codes from RFC 5730. See RFC 5730 for full text.
|
||||
|
||||
- 0 System connection error
|
||||
- 1000 - 1500 Success
|
||||
- 2000 - 2308 Registrar did something silly
|
||||
- 2400 - 2500 Registry did something silly
|
||||
- 2501 - 2502 Something malicious or abusive may have occurred
|
||||
"""
|
||||
|
||||
TRANSPORT_ERROR = 0
|
||||
|
||||
COMMAND_COMPLETED_SUCCESSFULLY = 1000
|
||||
COMMAND_COMPLETED_SUCCESSFULLY_ACTION_PENDING = 1001
|
||||
COMMAND_COMPLETED_SUCCESSFULLY_NO_MESSAGES = 1300
|
||||
|
@ -67,6 +69,9 @@ class RegistryError(Exception):
|
|||
def should_retry(self):
|
||||
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
|
||||
def is_connection_error(self):
|
||||
return self.code is None
|
||||
|
|
|
@ -527,14 +527,14 @@ class DomainApplicationAdminForm(forms.ModelForm):
|
|||
current_state = application.status
|
||||
|
||||
# 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(
|
||||
application, models.DomainApplication._meta.get_field("status")
|
||||
)
|
||||
|
||||
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
|
||||
# from editing the domain application; otherwise, the form will be
|
||||
|
@ -650,10 +650,10 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
|||
|
||||
if (
|
||||
obj
|
||||
and original_obj.status == models.DomainApplication.APPROVED
|
||||
and original_obj.status == models.DomainApplication.ApplicationStatus.APPROVED
|
||||
and (
|
||||
obj.status == models.DomainApplication.REJECTED
|
||||
or obj.status == models.DomainApplication.INELIGIBLE
|
||||
obj.status == models.DomainApplication.ApplicationStatus.REJECTED
|
||||
or obj.status == models.DomainApplication.ApplicationStatus.INELIGIBLE
|
||||
)
|
||||
and not obj.domain_is_not_active()
|
||||
):
|
||||
|
@ -675,14 +675,14 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
|||
else:
|
||||
if obj.status != original_obj.status:
|
||||
status_method_mapping = {
|
||||
models.DomainApplication.STARTED: None,
|
||||
models.DomainApplication.SUBMITTED: obj.submit,
|
||||
models.DomainApplication.IN_REVIEW: obj.in_review,
|
||||
models.DomainApplication.ACTION_NEEDED: obj.action_needed,
|
||||
models.DomainApplication.APPROVED: obj.approve,
|
||||
models.DomainApplication.WITHDRAWN: obj.withdraw,
|
||||
models.DomainApplication.REJECTED: obj.reject,
|
||||
models.DomainApplication.INELIGIBLE: (obj.reject_with_prejudice),
|
||||
models.DomainApplication.ApplicationStatus.STARTED: None,
|
||||
models.DomainApplication.ApplicationStatus.SUBMITTED: obj.submit,
|
||||
models.DomainApplication.ApplicationStatus.IN_REVIEW: obj.in_review,
|
||||
models.DomainApplication.ApplicationStatus.ACTION_NEEDED: obj.action_needed,
|
||||
models.DomainApplication.ApplicationStatus.APPROVED: obj.approve,
|
||||
models.DomainApplication.ApplicationStatus.WITHDRAWN: obj.withdraw,
|
||||
models.DomainApplication.ApplicationStatus.REJECTED: obj.reject,
|
||||
models.DomainApplication.ApplicationStatus.INELIGIBLE: (obj.reject_with_prejudice),
|
||||
}
|
||||
selected_method = status_method_mapping.get(obj.status)
|
||||
if selected_method is None:
|
||||
|
@ -752,6 +752,7 @@ class TransitionDomainAdmin(ListHeaderAdmin):
|
|||
"domain_name",
|
||||
"status",
|
||||
"email_sent",
|
||||
"processed",
|
||||
]
|
||||
|
||||
search_fields = ["username", "domain_name"]
|
||||
|
|
|
@ -49,28 +49,28 @@ class DomainApplicationFixture:
|
|||
# },
|
||||
DA = [
|
||||
{
|
||||
"status": "started",
|
||||
"organization_name": "Example - Finished but not Submitted",
|
||||
"status": DomainApplication.ApplicationStatus.STARTED,
|
||||
"organization_name": "Example - Finished but not submitted",
|
||||
},
|
||||
{
|
||||
"status": "submitted",
|
||||
"organization_name": "Example - Submitted but pending Investigation",
|
||||
"status": DomainApplication.ApplicationStatus.SUBMITTED,
|
||||
"organization_name": "Example - Submitted but pending investigation",
|
||||
},
|
||||
{
|
||||
"status": "in review",
|
||||
"organization_name": "Example - In Investigation",
|
||||
"status": DomainApplication.ApplicationStatus.IN_REVIEW,
|
||||
"organization_name": "Example - In investigation",
|
||||
},
|
||||
{
|
||||
"status": "in review",
|
||||
"status": DomainApplication.ApplicationStatus.IN_REVIEW,
|
||||
"organization_name": "Example - Approved",
|
||||
},
|
||||
{
|
||||
"status": "withdrawn",
|
||||
"status": DomainApplication.ApplicationStatus.WITHDRAWN,
|
||||
"organization_name": "Example - Withdrawn",
|
||||
},
|
||||
{
|
||||
"status": "action needed",
|
||||
"organization_name": "Example - Action Needed",
|
||||
"status": DomainApplication.ApplicationStatus.ACTION_NEEDED,
|
||||
"organization_name": "Example - Action needed",
|
||||
},
|
||||
{
|
||||
"status": "rejected",
|
||||
|
@ -214,7 +214,9 @@ class DomainFixture(DomainApplicationFixture):
|
|||
|
||||
for user in users:
|
||||
# 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}")
|
||||
application.approve()
|
||||
application.save()
|
||||
|
|
|
@ -262,7 +262,7 @@ class OrganizationContactForm(RegistrarForm):
|
|||
validators=[
|
||||
RegexValidator(
|
||||
"^[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
|
||||
#
|
||||
# ALGORITHM_CHOICES are options for alg attribute in DS Data
|
||||
# ALGORITHM_CHOICES are options for alg attribute in DS data
|
||||
# reference:
|
||||
# https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
|
||||
ALGORITHM_CHOICES = [
|
||||
|
@ -18,7 +18,7 @@ ALGORITHM_CHOICES = [
|
|||
(15, "(15) Ed25519"),
|
||||
(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
|
||||
DIGEST_TYPE_CHOICES = [
|
||||
(1, "(1) SHA-1"),
|
||||
|
|
|
@ -28,6 +28,17 @@ class DomainAddUserForm(forms.Form):
|
|||
|
||||
email = forms.EmailField(label="Email")
|
||||
|
||||
def clean(self):
|
||||
"""clean form data by lowercasing email"""
|
||||
cleaned_data = super().clean()
|
||||
|
||||
# Lowercase the value of the 'email' field
|
||||
email_value = cleaned_data.get("email")
|
||||
if email_value:
|
||||
cleaned_data["email"] = email_value.lower()
|
||||
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class DomainNameserverForm(forms.Form):
|
||||
"""Form for changing nameservers."""
|
||||
|
@ -239,7 +250,7 @@ class DomainOrgNameAddressForm(forms.ModelForm):
|
|||
validators=[
|
||||
RegexValidator(
|
||||
"^[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 +313,7 @@ class DomainDnssecForm(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):
|
||||
"""
|
||||
|
|
|
@ -62,7 +62,7 @@ class Command(BaseCommand):
|
|||
DomainInvitation(
|
||||
email=email_address.lower(),
|
||||
domain=domain,
|
||||
status=DomainInvitation.INVITED,
|
||||
status=DomainInvitation.DomainInvitationStatus.INVITED,
|
||||
)
|
||||
)
|
||||
logger.info("Creating %d invitations", len(to_create))
|
||||
|
|
|
@ -536,8 +536,8 @@ class Command(BaseCommand):
|
|||
domain_name=new_entry_domain_name,
|
||||
)
|
||||
|
||||
if not existing_entry.processed:
|
||||
if existing_entry.status != new_entry_status:
|
||||
# DEBUG:
|
||||
TerminalHelper.print_conditional(
|
||||
debug_on,
|
||||
f"{TerminalColors.OKCYAN}"
|
||||
|
@ -549,6 +549,14 @@ class Command(BaseCommand):
|
|||
existing_entry.status = new_entry_status
|
||||
existing_entry.email_sent = new_entry_emailSent
|
||||
existing_entry.save()
|
||||
else:
|
||||
TerminalHelper.print_conditional(
|
||||
debug_on,
|
||||
f"{TerminalColors.YELLOW}"
|
||||
f"Skipping update on processed domain: {existing_entry}"
|
||||
f"{TerminalColors.ENDC}",
|
||||
)
|
||||
|
||||
except TransitionDomain.MultipleObjectsReturned:
|
||||
logger.info(
|
||||
f"{TerminalColors.FAIL}"
|
||||
|
@ -558,6 +566,7 @@ class Command(BaseCommand):
|
|||
f"----------TERMINATING----------"
|
||||
)
|
||||
sys.exit()
|
||||
|
||||
else:
|
||||
# no matching entry, make one
|
||||
new_entry = TransitionDomain(
|
||||
|
@ -565,6 +574,7 @@ class Command(BaseCommand):
|
|||
domain_name=new_entry_domain_name,
|
||||
status=new_entry_status,
|
||||
email_sent=new_entry_emailSent,
|
||||
processed=False,
|
||||
)
|
||||
to_create.append(new_entry)
|
||||
total_new_entries += 1
|
||||
|
|
|
@ -559,7 +559,8 @@ class Command(BaseCommand):
|
|||
debug_max_entries_to_parse,
|
||||
total_rows_parsed,
|
||||
):
|
||||
for transition_domain in TransitionDomain.objects.all():
|
||||
changed_transition_domains = TransitionDomain.objects.filter(processed=False)
|
||||
for transition_domain in changed_transition_domains:
|
||||
(
|
||||
target_domain_information,
|
||||
associated_domain,
|
||||
|
@ -644,7 +645,8 @@ class Command(BaseCommand):
|
|||
debug_max_entries_to_parse,
|
||||
total_rows_parsed,
|
||||
):
|
||||
for transition_domain in TransitionDomain.objects.all():
|
||||
changed_transition_domains = TransitionDomain.objects.filter(processed=False)
|
||||
for transition_domain in changed_transition_domains:
|
||||
# Create some local variables to make data tracing easier
|
||||
transition_domain_name = transition_domain.domain_name
|
||||
transition_domain_status = transition_domain.status
|
||||
|
@ -796,6 +798,7 @@ class Command(BaseCommand):
|
|||
|
||||
# First, save all Domain objects to the database
|
||||
Domain.objects.bulk_create(domains_to_create)
|
||||
|
||||
# DomainInvitation.objects.bulk_create(domain_invitations_to_create)
|
||||
|
||||
# TODO: this is to resolve an error where bulk_create
|
||||
|
@ -847,6 +850,15 @@ class Command(BaseCommand):
|
|||
)
|
||||
DomainInformation.objects.bulk_create(domain_information_to_create)
|
||||
|
||||
# Loop through the list of everything created, and mark it as processed
|
||||
for domain in domains_to_create:
|
||||
name = domain.name
|
||||
TransitionDomain.objects.filter(domain_name=name).update(processed=True)
|
||||
|
||||
# Loop through the list of everything updated, and mark it as processed
|
||||
for name in updated_domain_entries:
|
||||
TransitionDomain.objects.filter(domain_name=name).update(processed=True)
|
||||
|
||||
self.print_summary_of_findings(
|
||||
domains_to_create,
|
||||
updated_domain_entries,
|
||||
|
|
|
@ -155,13 +155,13 @@ class LoadExtraTransitionDomain:
|
|||
def update_transition_domain_models(self):
|
||||
"""Updates TransitionDomain objects based off the file content
|
||||
given in self.parsed_data_container"""
|
||||
all_transition_domains = TransitionDomain.objects.all()
|
||||
if not all_transition_domains.exists():
|
||||
raise ValueError("No TransitionDomain objects exist.")
|
||||
valid_transition_domains = TransitionDomain.objects.filter(processed=False)
|
||||
if not valid_transition_domains.exists():
|
||||
raise ValueError("No updatable TransitionDomain objects exist.")
|
||||
|
||||
updated_transition_domains = []
|
||||
failed_transition_domains = []
|
||||
for transition_domain in all_transition_domains:
|
||||
for transition_domain in valid_transition_domains:
|
||||
domain_name = transition_domain.domain_name
|
||||
updated_transition_domain = transition_domain
|
||||
try:
|
||||
|
@ -228,7 +228,7 @@ class LoadExtraTransitionDomain:
|
|||
# DATA INTEGRITY CHECK
|
||||
# Make sure every Transition Domain got updated
|
||||
total_transition_domains = len(updated_transition_domains)
|
||||
total_updates_made = TransitionDomain.objects.all().count()
|
||||
total_updates_made = TransitionDomain.objects.filter(processed=False).count()
|
||||
if total_transition_domains != total_updates_made:
|
||||
# noqa here for line length
|
||||
logger.error(
|
||||
|
@ -787,7 +787,7 @@ class OrganizationDataLoader:
|
|||
self.tds_to_update: List[TransitionDomain] = []
|
||||
|
||||
def update_organization_data_for_all(self):
|
||||
"""Updates org address data for all TransitionDomains"""
|
||||
"""Updates org address data for valid TransitionDomains"""
|
||||
all_transition_domains = TransitionDomain.objects.all()
|
||||
if len(all_transition_domains) == 0:
|
||||
raise LoadOrganizationError(code=LoadOrganizationErrorCodes.EMPTY_TRANSITION_DOMAIN_TABLE)
|
||||
|
|
21
src/registrar/migrations/0055_transitiondomain_processed.py
Normal file
21
src/registrar/migrations/0055_transitiondomain_processed.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 4.2.7 on 2023-12-12 21:46
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("registrar", "0054_alter_domainapplication_federal_agency_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="transitiondomain",
|
||||
name="processed",
|
||||
field=models.BooleanField(
|
||||
default=True,
|
||||
help_text="Indicates whether this TransitionDomain was already processed",
|
||||
verbose_name="Processed",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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", "0055_transitiondomain_processed"),
|
||||
]
|
||||
|
||||
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."""
|
||||
|
||||
# the state is indeterminate
|
||||
UNKNOWN = "unknown"
|
||||
UNKNOWN = "unknown", "Unknown"
|
||||
|
||||
# The domain object exists in the registry
|
||||
# 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
|
||||
READY = "ready"
|
||||
READY = "ready", "Ready"
|
||||
|
||||
# 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
|
||||
DELETED = "deleted"
|
||||
DELETED = "deleted", "Deleted"
|
||||
|
||||
class Cache(property):
|
||||
"""
|
||||
|
@ -174,7 +174,8 @@ class Domain(TimeStampedModel, DomainHelper):
|
|||
"""Check if a domain is available."""
|
||||
if not cls.string_could_be_domain(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
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -19,25 +19,16 @@ class DomainApplication(TimeStampedModel):
|
|||
|
||||
"""A registrant's application for a new domain."""
|
||||
|
||||
# #### Constants for choice fields ####
|
||||
STARTED = "started"
|
||||
SUBMITTED = "submitted"
|
||||
IN_REVIEW = "in review"
|
||||
ACTION_NEEDED = "action needed"
|
||||
APPROVED = "approved"
|
||||
WITHDRAWN = "withdrawn"
|
||||
REJECTED = "rejected"
|
||||
INELIGIBLE = "ineligible"
|
||||
STATUS_CHOICES = [
|
||||
(STARTED, STARTED),
|
||||
(SUBMITTED, SUBMITTED),
|
||||
(IN_REVIEW, IN_REVIEW),
|
||||
(ACTION_NEEDED, ACTION_NEEDED),
|
||||
(APPROVED, APPROVED),
|
||||
(WITHDRAWN, WITHDRAWN),
|
||||
(REJECTED, REJECTED),
|
||||
(INELIGIBLE, INELIGIBLE),
|
||||
]
|
||||
# Constants for choice fields
|
||||
class ApplicationStatus(models.TextChoices):
|
||||
STARTED = "started", "Started"
|
||||
SUBMITTED = "submitted", "Submitted"
|
||||
IN_REVIEW = "in review", "In review"
|
||||
ACTION_NEEDED = "action needed", "Action needed"
|
||||
APPROVED = "approved", "Approved"
|
||||
WITHDRAWN = "withdrawn", "Withdrawn"
|
||||
REJECTED = "rejected", "Rejected"
|
||||
INELIGIBLE = "ineligible", "Ineligible"
|
||||
|
||||
class StateTerritoryChoices(models.TextChoices):
|
||||
ALABAMA = "AL", "Alabama (AL)"
|
||||
|
@ -363,8 +354,8 @@ class DomainApplication(TimeStampedModel):
|
|||
|
||||
# #### Internal fields about the application #####
|
||||
status = FSMField(
|
||||
choices=STATUS_CHOICES, # possible states as an array of constants
|
||||
default=STARTED, # sensible default
|
||||
choices=ApplicationStatus.choices, # possible states as an array of constants
|
||||
default=ApplicationStatus.STARTED, # sensible default
|
||||
protected=False, # can change state directly, particularly in Django admin
|
||||
)
|
||||
# This is the application user who created this application. The contact
|
||||
|
@ -592,7 +583,11 @@ class DomainApplication(TimeStampedModel):
|
|||
except EmailSendingError:
|
||||
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):
|
||||
"""Submit an application that is started.
|
||||
|
||||
|
@ -618,7 +613,7 @@ class DomainApplication(TimeStampedModel):
|
|||
"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):
|
||||
"""Investigate an application that has been submitted.
|
||||
|
||||
|
@ -630,7 +625,11 @@ class DomainApplication(TimeStampedModel):
|
|||
"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):
|
||||
"""Send back an application that is under investigation or rejected.
|
||||
|
||||
|
@ -644,8 +643,13 @@ class DomainApplication(TimeStampedModel):
|
|||
|
||||
@transition(
|
||||
field="status",
|
||||
source=[SUBMITTED, IN_REVIEW, REJECTED, INELIGIBLE],
|
||||
target=APPROVED,
|
||||
source=[
|
||||
ApplicationStatus.SUBMITTED,
|
||||
ApplicationStatus.IN_REVIEW,
|
||||
ApplicationStatus.REJECTED,
|
||||
ApplicationStatus.INELIGIBLE,
|
||||
],
|
||||
target=ApplicationStatus.APPROVED,
|
||||
)
|
||||
def approve(self):
|
||||
"""Approve an application that has been submitted.
|
||||
|
@ -678,7 +682,11 @@ class DomainApplication(TimeStampedModel):
|
|||
"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):
|
||||
"""Withdraw an application that has been submitted."""
|
||||
self._send_status_update_email(
|
||||
|
@ -689,8 +697,8 @@ class DomainApplication(TimeStampedModel):
|
|||
|
||||
@transition(
|
||||
field="status",
|
||||
source=[IN_REVIEW, APPROVED],
|
||||
target=REJECTED,
|
||||
source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.APPROVED],
|
||||
target=ApplicationStatus.REJECTED,
|
||||
conditions=[domain_is_not_active],
|
||||
)
|
||||
def reject(self):
|
||||
|
@ -698,7 +706,7 @@ class DomainApplication(TimeStampedModel):
|
|||
|
||||
As side effects this will delete the domain and domain_information
|
||||
(will cascade), and send an email notification."""
|
||||
if self.status == self.APPROVED:
|
||||
if self.status == self.ApplicationStatus.APPROVED:
|
||||
domain_state = self.approved_domain.state
|
||||
# Only reject if it exists on EPP
|
||||
if domain_state != Domain.State.UNKNOWN:
|
||||
|
@ -714,8 +722,8 @@ class DomainApplication(TimeStampedModel):
|
|||
|
||||
@transition(
|
||||
field="status",
|
||||
source=[IN_REVIEW, APPROVED],
|
||||
target=INELIGIBLE,
|
||||
source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.APPROVED],
|
||||
target=ApplicationStatus.INELIGIBLE,
|
||||
conditions=[domain_is_not_active],
|
||||
)
|
||||
def reject_with_prejudice(self):
|
||||
|
@ -727,7 +735,7 @@ class DomainApplication(TimeStampedModel):
|
|||
permissions classes test against. This will also delete the domain
|
||||
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
|
||||
# Only reject if it exists on EPP
|
||||
if domain_state != Domain.State.UNKNOWN:
|
||||
|
|
|
@ -15,8 +15,10 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class DomainInvitation(TimeStampedModel):
|
||||
INVITED = "invited"
|
||||
RETRIEVED = "retrieved"
|
||||
# Constants for status field
|
||||
class DomainInvitationStatus(models.TextChoices):
|
||||
INVITED = "invited", "Invited"
|
||||
RETRIEVED = "retrieved", "Retrieved"
|
||||
|
||||
email = models.EmailField(
|
||||
null=False,
|
||||
|
@ -31,18 +33,15 @@ class DomainInvitation(TimeStampedModel):
|
|||
)
|
||||
|
||||
status = FSMField(
|
||||
choices=[
|
||||
(INVITED, INVITED),
|
||||
(RETRIEVED, RETRIEVED),
|
||||
],
|
||||
default=INVITED,
|
||||
choices=DomainInvitationStatus.choices,
|
||||
default=DomainInvitationStatus.INVITED,
|
||||
protected=True, # can't alter state except through transition methods!
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
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):
|
||||
"""When an invitation is retrieved, create the corresponding permission.
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ from .utility.time_stamped_model import TimeStampedModel
|
|||
|
||||
class StatusChoices(models.TextChoices):
|
||||
READY = "ready", "Ready"
|
||||
ON_HOLD = "on hold", "On Hold"
|
||||
ON_HOLD = "on hold", "On hold"
|
||||
UNKNOWN = "unknown", "Unknown"
|
||||
|
||||
|
||||
|
@ -43,6 +43,12 @@ class TransitionDomain(TimeStampedModel):
|
|||
verbose_name="email sent",
|
||||
help_text="indicates whether email was sent",
|
||||
)
|
||||
processed = models.BooleanField(
|
||||
null=False,
|
||||
default=True,
|
||||
verbose_name="Processed",
|
||||
help_text="Indicates whether this TransitionDomain was already processed",
|
||||
)
|
||||
organization_type = models.TextField(
|
||||
max_length=255,
|
||||
null=True,
|
||||
|
|
|
@ -91,7 +91,8 @@ class User(AbstractUser):
|
|||
|
||||
# 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").
|
||||
if DomainInvitation.objects.filter(email=email, status=DomainInvitation.INVITED).exists():
|
||||
invited = DomainInvitation.DomainInvitationStatus.INVITED
|
||||
if DomainInvitation.objects.filter(email=email, status=invited).exists():
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -99,7 +100,9 @@ class User(AbstractUser):
|
|||
def check_domain_invitations_on_login(self):
|
||||
"""When a user first arrives on the site, we need to retrieve any domain
|
||||
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__iexact=self.email, status=DomainInvitation.DomainInvitationStatus.INVITED
|
||||
):
|
||||
try:
|
||||
invitation.retrieve()
|
||||
invitation.save()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Thanks for your domain request!{% endblock %}
|
||||
{% block title %}Thanks for your domain request! | {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" class="grid-container register-form-step">
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
Status:
|
||||
</span>
|
||||
{% 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 == 'submitted' %} Submitted
|
||||
{% elif domainapplication.status == 'ineligible' %} Ineligible
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% 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 %}
|
||||
|
||||
{% block content %}
|
||||
|
|
|
@ -149,7 +149,7 @@
|
|||
{% block logo %}
|
||||
<div class="usa-logo display-inline-block" id="extended-logo">
|
||||
<strong class="usa-logo__text" >
|
||||
<a href="{% url 'home' %}">.gov registrar </a>
|
||||
<a href="{% url 'home' %}">.gov Registrar </a>
|
||||
</strong>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "domain_base.html" %}
|
||||
{% load static field_helpers %}
|
||||
|
||||
{% block title %}Add another user{% endblock %}
|
||||
{% block title %}Add another user | {% endblock %}
|
||||
|
||||
{% block domain_content %}
|
||||
<h1>Add a domain manager</h1>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
Status:
|
||||
</span>
|
||||
{% if domain.state == domain.State.UNKNOWN or domain.state == domain.State.DNS_NEEDED%}
|
||||
DNS Needed
|
||||
DNS needed
|
||||
{% else %}
|
||||
{{ domain.state|title }}
|
||||
{% endif %}
|
||||
|
|
|
@ -12,9 +12,11 @@
|
|||
<p>You can enter your name servers, as well as other DNS-related information, in the following sections:</p>
|
||||
|
||||
{% url 'domain-dns-nameservers' pk=domain.id as url %}
|
||||
<p><a href="{{ url }}">DNS name servers</a></p>
|
||||
<ul>
|
||||
<li><a href="{{ url }}">Name servers</a></li>
|
||||
|
||||
{% url 'domain-dns-dnssec' pk=domain.id as url %}
|
||||
<p><a href="{{ url }}">DNSSEC</a></p>
|
||||
<li><a href="{{ url }}">DNSSEC</a></li>
|
||||
</ul>
|
||||
|
||||
{% endblock %} {# domain_content #}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
{% extends "domain_base.html" %}
|
||||
{% load static field_helpers url_helpers %}
|
||||
|
||||
{% block title %}DS Data | {{ domain.name }} | {% endblock %}
|
||||
{% block title %}DS data | {{ domain.name }} | {% endblock %}
|
||||
|
||||
{% block domain_content %}
|
||||
{% if domain.dnssecdata is None %}
|
||||
<div class="usa-alert usa-alert--info usa-alert--slim margin-bottom-3">
|
||||
<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>
|
||||
{% endif %}
|
||||
|
@ -16,11 +16,11 @@
|
|||
{% include "includes/form_errors.html" with form=form %}
|
||||
{% 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>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" %}
|
||||
|
||||
|
@ -31,9 +31,9 @@
|
|||
{% for form in formset %}
|
||||
<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="tablet:grid-col-4">
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
<a href="{{ url }}"
|
||||
{% if request.path == url %}class="usa-current"{% endif %}
|
||||
>
|
||||
DS Data
|
||||
DS data
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -14,7 +14,7 @@ Now that your .gov domain has been approved, there are a few more things to do b
|
|||
YOU MUST ADD DOMAIN NAME SERVER INFORMATION
|
||||
|
||||
Before your .gov domain can be used, you have to connect it to your Domain Name System (DNS) hosting service. At this time, we don’t provide DNS hosting services.
|
||||
Go to the domain management page to add your domain name server information <https://registrar.get.gov/domain/{{ application.id }}/nameservers>.
|
||||
Go to the domain management page to add your domain name server information <https://manage.get.gov/domain/{{ application.approved_domain.id }}/nameservers>.
|
||||
|
||||
Get help with adding your domain name server information <https://get.gov/help/domain-management/#manage-dns-information-for-your-domain>.
|
||||
|
||||
|
@ -23,7 +23,7 @@ ADD DOMAIN MANAGERS, SECURITY EMAIL
|
|||
|
||||
We strongly recommend that you add other points of contact who will help manage your domain. We also recommend that you provide a security email. This email will allow the public to report security issues on your domain. Security emails are made public.
|
||||
|
||||
Go to the domain management page to add domain contacts <https://registrar.get.gov/domain/{{ application.id }}/your-contact-information> and a security email <https://registrar.get.gov/domain/{{ application.id }}/security-email>.
|
||||
Go to the domain management page to add domain contacts <https://manage.get.gov/domain/{{ application.approved_domain.id }}/your-contact-information> and a security email <https://manage.get.gov/domain/{{ application.approved_domain.id }}/security-email>.
|
||||
|
||||
Get help with managing your .gov domain <https://get.gov/help/domain-management/>.
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ NEXT STEPS
|
|||
- We’re reviewing your request. This usually takes 20 business days.
|
||||
|
||||
- You can check the status of your request at any time.
|
||||
<https://registrar.get.gov/application/{{ application.id }}>
|
||||
<https://manage.get.gov/application/{{ application.id }}>
|
||||
|
||||
- We’ll email you with questions or when we complete our review.
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ NEXT STEPS
|
|||
- We’ll review your request. This usually takes 20 business days.
|
||||
|
||||
- You can check the status of your request at any time.
|
||||
<https://registrar.get.gov/application/{{ application.id }}>
|
||||
<https://manage.get.gov/application/{{ application.id }}>
|
||||
|
||||
- We’ll email you with questions or when we complete our review.
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
{% load static %}
|
||||
|
||||
{% block title %} Hello {% endblock %}
|
||||
{% block title %} Home | {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<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-label="Status">
|
||||
{% if domain.state == "unknown" or domain.state == "dns needed"%}
|
||||
DNS Needed
|
||||
DNS needed
|
||||
{% else %}
|
||||
{{ domain.state|title }}
|
||||
{% endif %}
|
||||
|
@ -87,7 +87,7 @@
|
|||
aria-live="polite"
|
||||
></div>
|
||||
{% else %}
|
||||
<p>You don't have any registered domains yet</p>
|
||||
<p>You don't have any registered domains.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
|
||||
|
@ -95,7 +95,7 @@
|
|||
<h2>Domain requests</h2>
|
||||
{% if domain_applications %}
|
||||
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked">
|
||||
<caption class="sr-only">Your domain applications</caption>
|
||||
<caption class="sr-only">Your domain requests</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-sortable scope="col" role="columnheader">Domain name</th>
|
||||
|
@ -111,7 +111,7 @@
|
|||
{{ application.requested_domain.name|default:"New domain request" }}
|
||||
</th>
|
||||
<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>
|
||||
{% if application.status == "started" or application.status == "action needed" or application.status == "withdrawn" %}
|
||||
<a href="{% url 'edit-application' application.pk %}">
|
||||
|
@ -138,7 +138,7 @@
|
|||
aria-live="polite"
|
||||
></div>
|
||||
{% else %}
|
||||
<p>You don't have any active domain requests right now</p>
|
||||
<p>You haven't requested any domains.</p>
|
||||
<!-- <p><a href="{% url 'application:' %}" class="usa-button">Start a new domain request</a></p> -->
|
||||
{% endif %}
|
||||
</section>
|
||||
|
|
|
@ -1,50 +1,62 @@
|
|||
{% if is_federal %}
|
||||
{% if federal_type == 'executive' %}
|
||||
<p>Domain requests from executive branch agencies must be authorized by <strong>Chief Information Officers</strong> or <strong>agency heads</strong>.</p>
|
||||
<p>Domain requests from executive branch agencies are subject to guidance issued by the U.S. Office of Management and Budget. </p>
|
||||
<h3>Executive branch federal agencies</h3>
|
||||
<p>Domain requests from executive branch federal agencies must be authorized by the agency's CIO or the head of the agency.</p>
|
||||
<p>See <a class="usa-link usa-link--external" rel="noopener noreferrer" target="_blank" href="https://www.whitehouse.gov/wp-content/uploads/2023/02/M-23-10-DOTGOV-Act-Guidance.pdf">OMB Memorandum M-23-10</a> for more information.</p>
|
||||
|
||||
{% elif federal_type == 'judicial' %}
|
||||
<p>Domain requests from the U.S. Supreme Court must be authorized by the <strong>director of information technology for the U.S. Supreme Court.</strong></p>
|
||||
<p>Domain requests from other judicial branch agencies must be authorized by the <strong>director</strong> or <strong>Chief Information Officer of the Administrative Office (AO)</strong> of the United States Courts.
|
||||
</p>
|
||||
<h3>Judicial branch federal agencies</h3>
|
||||
<p>Domain requests for judicial branch federal agencies, except the U.S. Supreme Court, must be authorized by the director or CIO of the Administrative Office (AO) of the United States Courts.</p>
|
||||
<p>Domain requests from the U.S. Supreme Court must be authorized by the director of information technology for the U.S. Supreme Court.</p>
|
||||
|
||||
{% elif federal_type == 'legislative' %}
|
||||
<h3> U.S. Senate </h3>
|
||||
<p>Domain requests from the U.S. Senate must come from the <strong>Senate Sergeant at Arms.</strong></p>
|
||||
<h3>Legislative branch federal agencies</h3>
|
||||
|
||||
<h3> U.S. House of Representatives </h3>
|
||||
<p class="">Domain requests from the U.S. House of Representatives must come from the <strong>House Chief Administrative Officer.</strong>
|
||||
<h4>U.S. Senate</h4>
|
||||
<p>Domain requests from the U.S. Senate must come from the Senate Sergeant at Arms.</p>
|
||||
|
||||
<h3> Other legislative branch agencies </h3>
|
||||
<p class="margin-top-1">Domain requests from legislative branch agencies must come from the <strong>agency’s head</strong> or <strong>Chief Information Officer.</strong></p>
|
||||
<p class="margin-top-1">Domain requests from legislative commissions must come from the <strong>head of the commission</strong>, or the <strong>head or Chief Information Officer of the parent agency,</strong> if there is one.
|
||||
</p>
|
||||
<h4>U.S. House of Representatives</h4>
|
||||
<p>Domain requests from the U.S. House of Representatives must come from the House Chief Administrative Officer.</p>
|
||||
|
||||
<h4>Other legislative branch agencies</h4>
|
||||
<p>Domain requests from legislative branch agencies must come from the agency’s head or CIO.</p>
|
||||
<p>Domain requests from legislative commissions must come from the head of the commission, or the head or CIO of the parent agency, if there is one.</p>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% elif organization_type == 'city' %}
|
||||
<p>Domain requests from cities must be authorized by <strong>the mayor</strong> or the equivalent <strong>highest-elected official.</strong></p>
|
||||
<h3>Cities</h3>
|
||||
<p>Domain requests from cities must be authorized by someone in a role of significant, executive responsibility within the city (mayor, council president, city manager, township/village supervisor, select board chairperson, chief, senior technology officer, or equivalent).</p>
|
||||
|
||||
{% elif organization_type == 'county' %}
|
||||
<p>Domain requests from counties must be authorized by the <strong>chair of the county commission </strong>or <strong>the equivalent highest-elected official.</strong></p>
|
||||
<h3>Counties</h3>
|
||||
<p>Domain requests from counties must be authorized by the commission chair or someone in a role of significant, executive responsibility within the county (county judge, county mayor, parish/borough president, senior technology officer, or equivalent). Other county-level offices (county clerk, sheriff, county auditor, comptroller) may qualify, as well, in some instances.</p>
|
||||
|
||||
{% elif organization_type == 'interstate' %}
|
||||
<p>Domain requests from interstate organizations must be authorized by the <strong>highest-ranking executive</strong> (president, director, chair, or equivalent) or <strong>one of the state’s governors or Chief Information Officers.</strong></p>
|
||||
<h3>Interstate organizations</h3>
|
||||
<p>Domain requests from interstate organizations must be authorized by someone in a role of significant, executive responsibility within the organization (president, director, chair, senior technology officer, or equivalent) or one of the state’s governors or CIOs.</p>
|
||||
|
||||
{% elif organization_type == 'school_district' %}
|
||||
<p>Domain requests from school district governments must be authorized by the <strong>highest-ranking executive (the chair of a school district’s board or a superintendent)</strong>.</p>
|
||||
<h3>School districts</h3>
|
||||
<p>Domain requests from school district governments must be authorized by someone in a role of significant, executive responsibility within the district (board chair, superintendent, senior technology officer, or equivalent).</p>
|
||||
|
||||
{% elif organization_type == 'special_district' %}
|
||||
<p>Domain requests from special districts must be authorized by the <strong>highest-ranking executive (president, director, chair, or equivalent)</strong> or <strong>state Chief Information Officers for state-based organizations</strong>.</p>
|
||||
<h3>Special districts</h3>
|
||||
<p>Domain requests from special districts must be authorized by someone in a role of significant, executive responsibility within the district (CEO, chair, executive director, senior technology officer, or equivalent).
|
||||
</p>
|
||||
|
||||
{% elif organization_type == 'state_or_territory' %}
|
||||
<h3>States and territories: executive branch</h3>
|
||||
<p>Domain requests from states and territories must be authorized by the <strong>governor</strong> or the <strong>state Chief Information Officer.</strong></p>
|
||||
<h3>States and territories: judicial and legislative branches</h3>
|
||||
<p>Domain requests from state legislatures and courts must be authorized by an agency’s <strong>Chief Information Officer</strong> or <strong>highest-ranking executive</strong>.</p>
|
||||
<h3>U.S. states and territories</h3>
|
||||
|
||||
<h4>States and territories: executive branch</h4>
|
||||
<p>Domain requests from states and territories must be authorized by the governor or someone in a role of significant, executive responsibility within the agency (department secretary, senior technology officer, or equivalent).</p>
|
||||
|
||||
<h4>States and territories: judicial and legislative branches</h4>
|
||||
<p>Domain requests from state legislatures and courts must be authorized by an agency’s CIO or someone in a role of significant, executive responsibility within the agency.</p>
|
||||
|
||||
{% elif organization_type == 'tribal' %}
|
||||
<p><strong>Domain requests from federally-recognized tribal governments must be authorized by the leader of the tribe</strong>, as recognized by the <a class="usa-link usa-link--external" rel="noopener noreferrer" target="_blank" href="https://www.bia.gov/service/tribal-leaders-directory">Bureau of Indian Affairs</a>.</p>
|
||||
<p><strong>Domain requests from state-recognized tribal governments must be authorized by the leader of the tribe</strong>, as determined by the state’s tribal recognition initiative.</p>
|
||||
<h3>Tribal governments</h3>
|
||||
<p>Domain requests from federally-recognized tribal governments must be authorized by the tribal leader the <a class="usa-link usa-link--external" rel="noopener noreferrer" target="_blank" href="https://www.bia.gov/service/tribal-leaders-directory">Bureau of Indian Affairs</a> recognizes.</p>
|
||||
<p>Domain requests from state-recognized tribal governments must be authorized by the tribal leader the individual state recognizes.</p>
|
||||
|
||||
{% endif %}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}
|
||||
Edit your User Profile
|
||||
Edit your User Profile |
|
||||
{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
|
|
|
@ -295,7 +295,7 @@ class AuditedAdminMockData:
|
|||
self,
|
||||
domain_type,
|
||||
item_name,
|
||||
status=DomainApplication.STARTED,
|
||||
status=DomainApplication.ApplicationStatus.STARTED,
|
||||
org_type="federal",
|
||||
federal_type="executive",
|
||||
purpose="Purpose of the site",
|
||||
|
@ -312,7 +312,7 @@ class AuditedAdminMockData:
|
|||
title, email, and username.
|
||||
|
||||
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
|
||||
|
||||
|
@ -345,23 +345,23 @@ class AuditedAdminMockData:
|
|||
full_arg_dict = dict(
|
||||
email="test_mail@mail.com",
|
||||
domain=self.dummy_domain(item_name, True),
|
||||
status=DomainInvitation.INVITED,
|
||||
status=DomainInvitation.DomainInvitationStatus.INVITED,
|
||||
)
|
||||
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"""
|
||||
domain_application_kwargs = self.dummy_kwarg_boilerplate(self.APPLICATION, item_name, status)
|
||||
application = DomainApplication.objects.get_or_create(**domain_application_kwargs)[0]
|
||||
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"""
|
||||
domain_application_kwargs = self.dummy_kwarg_boilerplate(self.INFORMATION, item_name, status)
|
||||
application = DomainInformation.objects.get_or_create(**domain_application_kwargs)[0]
|
||||
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"""
|
||||
domain_application_kwargs = self.dummy_kwarg_boilerplate(self.INVITATION, item_name, status)
|
||||
application = DomainInvitation.objects.get_or_create(**domain_application_kwargs)[0]
|
||||
|
@ -375,7 +375,7 @@ class AuditedAdminMockData:
|
|||
has_other_contacts=True,
|
||||
has_current_website=True,
|
||||
has_alternative_gov_domain=True,
|
||||
status=DomainApplication.STARTED,
|
||||
status=DomainApplication.ApplicationStatus.STARTED,
|
||||
):
|
||||
"""A helper to create a dummy domain application object"""
|
||||
application = None
|
||||
|
@ -456,7 +456,7 @@ def completed_application(
|
|||
has_alternative_gov_domain=True,
|
||||
has_about_your_organization=True,
|
||||
has_anything_else=True,
|
||||
status=DomainApplication.STARTED,
|
||||
status=DomainApplication.ApplicationStatus.STARTED,
|
||||
user=False,
|
||||
name="city.gov",
|
||||
):
|
||||
|
@ -830,10 +830,8 @@ class MockEppLib(TestCase):
|
|||
def mockCheckDomainCommand(self, _request, cleaned):
|
||||
if "gsa.gov" in getattr(_request, "names", None):
|
||||
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):
|
||||
return self._mockDomainName("igorvilleremixed.gov", True)
|
||||
return self._mockDomainName("igorville.gov", True)
|
||||
elif "top-level-agency.gov" in getattr(_request, "names", None):
|
||||
return self._mockDomainName("top-level-agency.gov", True)
|
||||
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
|
||||
"""
|
||||
self.client.force_login(self.superuser)
|
||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||
application.approve()
|
||||
|
||||
response = self.client.get("/admin/registrar/domain/")
|
||||
|
@ -282,7 +282,7 @@ class TestDomainApplicationAdminForm(TestCase):
|
|||
form = DomainApplicationAdminForm(instance=self.application)
|
||||
|
||||
# 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)
|
||||
|
||||
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))
|
||||
|
||||
# Modify the application's property
|
||||
application.status = DomainApplication.SUBMITTED
|
||||
application.status = DomainApplication.ApplicationStatus.SUBMITTED
|
||||
|
||||
# Use the model admin's save_model method
|
||||
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):
|
||||
# Create a sample application
|
||||
application = completed_application(status=DomainApplication.SUBMITTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED)
|
||||
|
||||
# Create a mock request
|
||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||
|
||||
# Modify the application's property
|
||||
application.status = DomainApplication.IN_REVIEW
|
||||
application.status = DomainApplication.ApplicationStatus.IN_REVIEW
|
||||
|
||||
# Use the model admin's save_model method
|
||||
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):
|
||||
# Create a sample application
|
||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||
|
||||
# Create a mock request
|
||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||
|
||||
# Modify the application's property
|
||||
application.status = DomainApplication.APPROVED
|
||||
application.status = DomainApplication.ApplicationStatus.APPROVED
|
||||
|
||||
# Use the model admin's save_model method
|
||||
self.admin.save_model(request, application, form=None, change=True)
|
||||
|
@ -467,13 +467,13 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
User.objects.filter(email=EMAIL).delete()
|
||||
|
||||
# Create a sample application
|
||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||
|
||||
# Create a mock request
|
||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||
|
||||
# Modify the application's property
|
||||
application.status = DomainApplication.APPROVED
|
||||
application.status = DomainApplication.ApplicationStatus.APPROVED
|
||||
|
||||
# Use the model admin's save_model method
|
||||
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):
|
||||
# Create a sample application
|
||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||
|
||||
# Create a mock request
|
||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||
|
||||
# Modify the application's property
|
||||
application.status = DomainApplication.ACTION_NEEDED
|
||||
application.status = DomainApplication.ApplicationStatus.ACTION_NEEDED
|
||||
|
||||
# Use the model admin's save_model method
|
||||
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):
|
||||
# Create a sample application
|
||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||
|
||||
# Create a mock request
|
||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||
|
||||
# Modify the application's property
|
||||
application.status = DomainApplication.REJECTED
|
||||
application.status = DomainApplication.ApplicationStatus.REJECTED
|
||||
|
||||
# Use the model admin's save_model method
|
||||
self.admin.save_model(request, application, form=None, change=True)
|
||||
|
@ -569,13 +569,13 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
User.objects.filter(email=EMAIL).delete()
|
||||
|
||||
# Create a sample application
|
||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||
|
||||
# Create a mock request
|
||||
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||
|
||||
# Modify the application's property
|
||||
application.status = DomainApplication.INELIGIBLE
|
||||
application.status = DomainApplication.ApplicationStatus.INELIGIBLE
|
||||
|
||||
# Use the model admin's save_model method
|
||||
self.admin.save_model(request, application, form=None, change=True)
|
||||
|
@ -584,7 +584,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
self.assertEqual(application.creator.status, "restricted")
|
||||
|
||||
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.save()
|
||||
|
||||
|
@ -662,7 +662,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
|
||||
def test_saving_when_restricted_creator(self):
|
||||
# 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.save()
|
||||
|
||||
|
@ -681,11 +681,11 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
)
|
||||
|
||||
# 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):
|
||||
# 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.save()
|
||||
|
||||
|
@ -704,7 +704,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
|
||||
def test_error_when_saving_approved_to_rejected_and_domain_is_active(self):
|
||||
# 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)
|
||||
application.approved_domain = domain
|
||||
application.save()
|
||||
|
@ -724,7 +724,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
stack.enter_context(patch.object(messages, "error"))
|
||||
|
||||
# Simulate saving the model
|
||||
application.status = DomainApplication.REJECTED
|
||||
application.status = DomainApplication.ApplicationStatus.REJECTED
|
||||
self.admin.save_model(request, application, None, True)
|
||||
|
||||
# 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):
|
||||
# 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_information = DomainInformation.objects.create(creator=self.superuser, domain=domain)
|
||||
application.approved_domain = domain
|
||||
|
@ -756,7 +756,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
stack.enter_context(patch.object(messages, "error"))
|
||||
|
||||
# Simulate saving the model
|
||||
application.status = DomainApplication.REJECTED
|
||||
application.status = DomainApplication.ApplicationStatus.REJECTED
|
||||
self.admin.save_model(request, application, None, True)
|
||||
|
||||
# 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):
|
||||
# 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)
|
||||
application.approved_domain = domain
|
||||
application.save()
|
||||
|
@ -794,7 +794,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
stack.enter_context(patch.object(messages, "error"))
|
||||
|
||||
# Simulate saving the model
|
||||
application.status = DomainApplication.INELIGIBLE
|
||||
application.status = DomainApplication.ApplicationStatus.INELIGIBLE
|
||||
self.admin.save_model(request, application, None, True)
|
||||
|
||||
# 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):
|
||||
# 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_information = DomainInformation.objects.create(creator=self.superuser, domain=domain)
|
||||
application.approved_domain = domain
|
||||
|
@ -826,7 +826,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
|||
stack.enter_context(patch.object(messages, "error"))
|
||||
|
||||
# Simulate saving the model
|
||||
application.status = DomainApplication.INELIGIBLE
|
||||
application.status = DomainApplication.ApplicationStatus.INELIGIBLE
|
||||
self.admin.save_model(request, application, None, True)
|
||||
|
||||
# Assert that the error message was never called
|
||||
|
@ -877,12 +877,14 @@ class DomainInvitationAdminTest(TestCase):
|
|||
)
|
||||
|
||||
# Assert that the filters are added
|
||||
self.assertContains(response, "invited", count=4)
|
||||
self.assertContains(response, "retrieved", count=4)
|
||||
self.assertContains(response, "invited", count=2)
|
||||
self.assertContains(response, "Invited", count=2)
|
||||
self.assertContains(response, "retrieved", count=2)
|
||||
self.assertContains(response, "Retrieved", count=2)
|
||||
|
||||
# Check for the HTML context specificially
|
||||
invited_html = '<a href="?status__exact=invited">invited</a>'
|
||||
retrieved_html = '<a href="?status__exact=retrieved">retrieved</a>'
|
||||
invited_html = '<a href="?status__exact=invited">Invited</a>'
|
||||
retrieved_html = '<a href="?status__exact=retrieved">Retrieved</a>'
|
||||
|
||||
self.assertContains(response, invited_html, count=1)
|
||||
self.assertContains(response, retrieved_html, count=1)
|
||||
|
|
|
@ -30,7 +30,7 @@ class TestFormValidation(MockEppLib):
|
|||
form = OrganizationContactForm(data={"zipcode": "nah"})
|
||||
self.assertEqual(
|
||||
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):
|
||||
|
|
|
@ -35,7 +35,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Can create with just a creator."""
|
||||
user, _ = User.objects.get_or_create()
|
||||
application = DomainApplication.objects.create(creator=user)
|
||||
self.assertEqual(application.status, DomainApplication.STARTED)
|
||||
self.assertEqual(application.status, DomainApplication.ApplicationStatus.STARTED)
|
||||
|
||||
def test_full_create(self):
|
||||
"""Can create with all fields."""
|
||||
|
@ -108,7 +108,7 @@ class TestDomainApplication(TestCase):
|
|||
# no submitter email so this emits a log warning
|
||||
with less_console_noise():
|
||||
application.submit()
|
||||
self.assertEqual(application.status, application.SUBMITTED)
|
||||
self.assertEqual(application.status, application.ApplicationStatus.SUBMITTED)
|
||||
|
||||
def test_submit_sends_email(self):
|
||||
"""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
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.SUBMITTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.submit()
|
||||
|
@ -148,7 +148,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status in review and call submit
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.submit()
|
||||
|
@ -157,7 +157,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status approved and call submit
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.APPROVED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.submit()
|
||||
|
@ -166,7 +166,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status rejected and call submit
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.REJECTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.submit()
|
||||
|
@ -175,7 +175,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status ineligible and call submit
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.INELIGIBLE)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.submit()
|
||||
|
@ -184,7 +184,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status started and call in_review
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.STARTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.in_review()
|
||||
|
@ -193,7 +193,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status in review and call in_review
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.in_review()
|
||||
|
@ -202,7 +202,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status approved and call in_review
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.APPROVED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.in_review()
|
||||
|
@ -211,7 +211,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status action needed and call in_review
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.ACTION_NEEDED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.in_review()
|
||||
|
@ -220,7 +220,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status rejected and call in_review
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.REJECTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.in_review()
|
||||
|
@ -229,7 +229,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status withdrawn and call in_review
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.WITHDRAWN)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.in_review()
|
||||
|
@ -238,7 +238,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status ineligible and call in_review
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.INELIGIBLE)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.in_review()
|
||||
|
@ -247,7 +247,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status started and call action_needed
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.STARTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.action_needed()
|
||||
|
@ -256,7 +256,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status submitted and call action_needed
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.SUBMITTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.action_needed()
|
||||
|
@ -265,7 +265,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status action needed and call action_needed
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.ACTION_NEEDED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.action_needed()
|
||||
|
@ -274,7 +274,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status approved and call action_needed
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.APPROVED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.action_needed()
|
||||
|
@ -283,7 +283,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status withdrawn and call action_needed
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.WITHDRAWN)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.action_needed()
|
||||
|
@ -292,7 +292,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status ineligible and call action_needed
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.INELIGIBLE)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.action_needed()
|
||||
|
@ -301,7 +301,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status started and call approve
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.STARTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.approve()
|
||||
|
@ -310,7 +310,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status approved and call approve
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.APPROVED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.approve()
|
||||
|
@ -319,7 +319,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status action needed and call approve
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.ACTION_NEEDED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.approve()
|
||||
|
@ -328,7 +328,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status withdrawn and call approve
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.WITHDRAWN)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.approve()
|
||||
|
@ -337,7 +337,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status started and call withdraw
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.STARTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.withdraw()
|
||||
|
@ -346,7 +346,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status approved and call withdraw
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.APPROVED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.withdraw()
|
||||
|
@ -355,7 +355,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status action needed and call withdraw
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.ACTION_NEEDED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.withdraw()
|
||||
|
@ -364,7 +364,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status rejected and call withdraw
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.REJECTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.withdraw()
|
||||
|
@ -373,7 +373,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status withdrawn and call withdraw
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.WITHDRAWN)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.withdraw()
|
||||
|
@ -382,7 +382,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status ineligible and call withdraw
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.INELIGIBLE)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.withdraw()
|
||||
|
@ -391,7 +391,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status started and call reject
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.STARTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.reject()
|
||||
|
@ -400,7 +400,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status submitted and call reject
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.SUBMITTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.reject()
|
||||
|
@ -409,7 +409,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status action needed and call reject
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.ACTION_NEEDED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.reject()
|
||||
|
@ -418,7 +418,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status withdrawn and call reject
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.WITHDRAWN)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.reject()
|
||||
|
@ -427,7 +427,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status rejected and call reject
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.REJECTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.reject()
|
||||
|
@ -436,7 +436,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status ineligible and call reject
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.INELIGIBLE)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.reject()
|
||||
|
@ -445,7 +445,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status approved, create a matching domain that
|
||||
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)
|
||||
application.approved_domain = domain
|
||||
application.save()
|
||||
|
@ -464,7 +464,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status started and call reject
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.STARTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.STARTED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.reject_with_prejudice()
|
||||
|
@ -473,7 +473,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status submitted and call reject
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.SUBMITTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.reject_with_prejudice()
|
||||
|
@ -482,7 +482,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status action needed and call reject
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.ACTION_NEEDED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.reject_with_prejudice()
|
||||
|
@ -491,7 +491,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status withdrawn and call reject
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.WITHDRAWN)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.reject_with_prejudice()
|
||||
|
@ -500,7 +500,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status rejected and call reject
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.REJECTED)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.reject_with_prejudice()
|
||||
|
@ -509,7 +509,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status ineligible and call reject
|
||||
against transition rules"""
|
||||
|
||||
application = completed_application(status=DomainApplication.INELIGIBLE)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE)
|
||||
|
||||
with self.assertRaises(TransitionNotAllowed):
|
||||
application.reject_with_prejudice()
|
||||
|
@ -518,7 +518,7 @@ class TestDomainApplication(TestCase):
|
|||
"""Create an application with status approved, create a matching domain that
|
||||
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)
|
||||
application.approved_domain = domain
|
||||
application.save()
|
||||
|
@ -543,7 +543,7 @@ class TestPermissions(TestCase):
|
|||
user, _ = User.objects.get_or_create()
|
||||
application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain)
|
||||
# skip using the submit method
|
||||
application.status = DomainApplication.SUBMITTED
|
||||
application.status = DomainApplication.ApplicationStatus.SUBMITTED
|
||||
application.approve()
|
||||
|
||||
# should be a role for this user
|
||||
|
@ -560,7 +560,7 @@ class TestDomainInfo(TestCase):
|
|||
user, _ = User.objects.get_or_create()
|
||||
application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain)
|
||||
# skip using the submit method
|
||||
application.status = DomainApplication.SUBMITTED
|
||||
application.status = DomainApplication.ApplicationStatus.SUBMITTED
|
||||
application.approve()
|
||||
|
||||
# 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
|
||||
with less_console_noise():
|
||||
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):
|
||||
"""A user's authenticate on_each_login callback retrieves their invitations."""
|
||||
|
@ -699,3 +699,17 @@ class TestContact(TestCase):
|
|||
# Updating the contact's email does not propagate
|
||||
self.assertEqual(self.contact.email, "joey.baloney@diaperville.com")
|
||||
self.assertEqual(self.user.email, "mayor@igorville.gov")
|
||||
|
||||
def test_check_domain_invitations_on_login_caps_email(self):
|
||||
"""A DomainInvitation with an email address with capital letters should match
|
||||
a User record whose email address is not in caps"""
|
||||
# create DomainInvitation with CAPS email that matches User email
|
||||
# on a case-insensitive match
|
||||
caps_email = "MAYOR@igorville.gov"
|
||||
# mock the domain invitation save routine
|
||||
with patch("registrar.models.DomainInvitation.save") as save_mock:
|
||||
DomainInvitation.objects.get_or_create(email=caps_email, domain=self.domain)
|
||||
self.user.check_domain_invitations_on_login()
|
||||
# if check_domain_invitations_on_login properly matches exactly one
|
||||
# Domain Invitation, then save routine should be called exactly once
|
||||
save_mock.assert_called_once()
|
||||
|
|
|
@ -261,7 +261,7 @@ class TestDomainCreation(MockEppLib):
|
|||
user, _ = User.objects.get_or_create()
|
||||
application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain)
|
||||
# skip using the submit method
|
||||
application.status = DomainApplication.SUBMITTED
|
||||
application.status = DomainApplication.ApplicationStatus.SUBMITTED
|
||||
# transition to approve state
|
||||
application.approve()
|
||||
# should have information present for this domain
|
||||
|
@ -1506,7 +1506,7 @@ class TestRegistrantNameservers(MockEppLib):
|
|||
]
|
||||
|
||||
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"""
|
||||
domain, _ = Domain.objects.get_or_create(name="onholdDomain.gov", state=Domain.State.ON_HOLD)
|
||||
with self.assertRaises(ActionNotAllowed):
|
||||
|
|
|
@ -258,7 +258,6 @@ class ExportDataTest(TestCase):
|
|||
)
|
||||
|
||||
def tearDown(self):
|
||||
# Dummy push - will remove
|
||||
Domain.objects.all().delete()
|
||||
DomainInformation.objects.all().delete()
|
||||
User.objects.all().delete()
|
||||
|
|
|
@ -21,6 +21,155 @@ from registrar.models.contact import Contact
|
|||
from .common import less_console_noise
|
||||
|
||||
|
||||
class TestProcessedMigrations(TestCase):
|
||||
"""This test case class is designed to verify the idempotency of migrations
|
||||
related to domain transitions in the application."""
|
||||
|
||||
def setUp(self):
|
||||
"""Defines the file name of migration_json and the folder its contained in"""
|
||||
self.test_data_file_location = "registrar/tests/data"
|
||||
self.migration_json_filename = "test_migrationFilepaths.json"
|
||||
self.user, _ = User.objects.get_or_create(username="igorvillian")
|
||||
|
||||
def tearDown(self):
|
||||
"""Deletes all DB objects related to migrations"""
|
||||
# Delete domain information
|
||||
Domain.objects.all().delete()
|
||||
DomainInformation.objects.all().delete()
|
||||
DomainInvitation.objects.all().delete()
|
||||
TransitionDomain.objects.all().delete()
|
||||
|
||||
# Delete users
|
||||
User.objects.all().delete()
|
||||
UserDomainRole.objects.all().delete()
|
||||
|
||||
def run_load_domains(self):
|
||||
"""
|
||||
This method executes the load_transition_domain command.
|
||||
|
||||
It uses 'unittest.mock.patch' to mock the TerminalHelper.query_yes_no_exit method,
|
||||
which is a user prompt in the terminal. The mock function always returns True,
|
||||
allowing the test to proceed without manual user input.
|
||||
|
||||
The 'call_command' function from Django's management framework is then used to
|
||||
execute the load_transition_domain command with the specified arguments.
|
||||
"""
|
||||
# noqa here because splitting this up makes it confusing.
|
||||
# ES501
|
||||
with patch(
|
||||
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa
|
||||
return_value=True,
|
||||
):
|
||||
call_command(
|
||||
"load_transition_domain",
|
||||
self.migration_json_filename,
|
||||
directory=self.test_data_file_location,
|
||||
)
|
||||
|
||||
def run_transfer_domains(self):
|
||||
"""
|
||||
This method executes the transfer_transition_domains_to_domains command.
|
||||
|
||||
The 'call_command' function from Django's management framework is then used to
|
||||
execute the load_transition_domain command with the specified arguments.
|
||||
"""
|
||||
call_command("transfer_transition_domains_to_domains")
|
||||
|
||||
def test_domain_idempotent(self):
|
||||
"""
|
||||
This test ensures that the domain transfer process
|
||||
is idempotent on Domain and DomainInformation.
|
||||
"""
|
||||
unchanged_domain, _ = Domain.objects.get_or_create(
|
||||
name="testdomain.gov",
|
||||
state=Domain.State.READY,
|
||||
expiration_date=datetime.date(2000, 1, 1),
|
||||
)
|
||||
unchanged_domain_information, _ = DomainInformation.objects.get_or_create(
|
||||
domain=unchanged_domain, organization_name="test org name", creator=self.user
|
||||
)
|
||||
self.run_load_domains()
|
||||
|
||||
# Test that a given TransitionDomain isn't set to "processed"
|
||||
transition_domain_object = TransitionDomain.objects.get(domain_name="fakewebsite3.gov")
|
||||
self.assertFalse(transition_domain_object.processed)
|
||||
|
||||
self.run_transfer_domains()
|
||||
|
||||
# Test that old data isn't corrupted
|
||||
actual_unchanged = Domain.objects.filter(name="testdomain.gov").get()
|
||||
actual_unchanged_information = DomainInformation.objects.filter(domain=actual_unchanged).get()
|
||||
self.assertEqual(unchanged_domain, actual_unchanged)
|
||||
self.assertEqual(unchanged_domain_information, actual_unchanged_information)
|
||||
|
||||
# Test that a given TransitionDomain is set to "processed" after we transfer domains
|
||||
transition_domain_object = TransitionDomain.objects.get(domain_name="fakewebsite3.gov")
|
||||
self.assertTrue(transition_domain_object.processed)
|
||||
|
||||
# Manually change Domain/DomainInformation objects
|
||||
changed_domain = Domain.objects.filter(name="fakewebsite3.gov").get()
|
||||
changed_domain.expiration_date = datetime.date(1999, 1, 1)
|
||||
|
||||
changed_domain.save()
|
||||
|
||||
changed_domain_information = DomainInformation.objects.filter(domain=changed_domain).get()
|
||||
changed_domain_information.organization_name = "changed"
|
||||
|
||||
changed_domain_information.save()
|
||||
|
||||
# Rerun transfer domains
|
||||
self.run_transfer_domains()
|
||||
|
||||
# Test that old data isn't corrupted after running this twice
|
||||
actual_unchanged = Domain.objects.filter(name="testdomain.gov").get()
|
||||
actual_unchanged_information = DomainInformation.objects.filter(domain=actual_unchanged).get()
|
||||
self.assertEqual(unchanged_domain, actual_unchanged)
|
||||
self.assertEqual(unchanged_domain_information, actual_unchanged_information)
|
||||
|
||||
# Ensure that domain hasn't changed
|
||||
actual_domain = Domain.objects.filter(name="fakewebsite3.gov").get()
|
||||
self.assertEqual(changed_domain, actual_domain)
|
||||
|
||||
# Ensure that DomainInformation hasn't changed
|
||||
actual_domain_information = DomainInformation.objects.filter(domain=changed_domain).get()
|
||||
self.assertEqual(changed_domain_information, actual_domain_information)
|
||||
|
||||
def test_transition_domain_is_processed(self):
|
||||
"""
|
||||
This test checks if a domain is correctly marked as processed in the transition.
|
||||
"""
|
||||
old_transition_domain, _ = TransitionDomain.objects.get_or_create(domain_name="testdomain.gov")
|
||||
# Asser that old records default to 'True'
|
||||
self.assertTrue(old_transition_domain.processed)
|
||||
|
||||
unchanged_domain, _ = Domain.objects.get_or_create(
|
||||
name="testdomain.gov",
|
||||
state=Domain.State.READY,
|
||||
expiration_date=datetime.date(2000, 1, 1),
|
||||
)
|
||||
unchanged_domain_information, _ = DomainInformation.objects.get_or_create(
|
||||
domain=unchanged_domain, organization_name="test org name", creator=self.user
|
||||
)
|
||||
self.run_load_domains()
|
||||
|
||||
# Test that a given TransitionDomain isn't set to "processed"
|
||||
transition_domain_object = TransitionDomain.objects.get(domain_name="fakewebsite3.gov")
|
||||
self.assertFalse(transition_domain_object.processed)
|
||||
|
||||
self.run_transfer_domains()
|
||||
|
||||
# Test that old data isn't corrupted
|
||||
actual_unchanged = Domain.objects.filter(name="testdomain.gov").get()
|
||||
actual_unchanged_information = DomainInformation.objects.filter(domain=actual_unchanged).get()
|
||||
self.assertEqual(unchanged_domain, actual_unchanged)
|
||||
self.assertTrue(old_transition_domain.processed)
|
||||
self.assertEqual(unchanged_domain_information, actual_unchanged_information)
|
||||
|
||||
# Test that a given TransitionDomain is set to "processed" after we transfer domains
|
||||
transition_domain_object = TransitionDomain.objects.get(domain_name="fakewebsite3.gov")
|
||||
self.assertTrue(transition_domain_object.processed)
|
||||
|
||||
|
||||
class TestOrganizationMigration(TestCase):
|
||||
def setUp(self):
|
||||
"""Defines the file name of migration_json and the folder its contained in"""
|
||||
|
|
|
@ -100,7 +100,7 @@ class LoggedInTests(TestWithUser):
|
|||
response = self.client.get("/")
|
||||
# count = 2 because it is also in screenreader content
|
||||
self.assertContains(response, "igorville.gov", count=2)
|
||||
self.assertContains(response, "DNS Needed")
|
||||
self.assertContains(response, "DNS needed")
|
||||
# clean up
|
||||
role.delete()
|
||||
|
||||
|
@ -804,7 +804,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
# ---- AO CONTACT PAGE ----
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
ao_page = org_contact_result.follow()
|
||||
self.assertContains(ao_page, "Domain requests from executive branch agencies")
|
||||
self.assertContains(ao_page, "Executive branch federal agencies")
|
||||
|
||||
# Go back to organization type page and change type
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
@ -1079,7 +1079,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
Make sure the long name is displaying in the application summary
|
||||
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("/")
|
||||
self.assertContains(home_page, "city.gov")
|
||||
# click the "Edit" link
|
||||
|
@ -1355,29 +1355,55 @@ class TestDomainManagers(TestDomainOverview):
|
|||
out the boto3 SES email sending here.
|
||||
"""
|
||||
# make sure there is no user with this email
|
||||
EMAIL = "mayor@igorville.gov"
|
||||
User.objects.filter(email=EMAIL).delete()
|
||||
email_address = "mayor@igorville.gov"
|
||||
User.objects.filter(email=email_address).delete()
|
||||
|
||||
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
|
||||
|
||||
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
add_page.form["email"] = EMAIL
|
||||
add_page.form["email"] = email_address
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
success_result = add_page.form.submit()
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
success_page = success_result.follow()
|
||||
|
||||
self.assertContains(success_page, EMAIL)
|
||||
self.assertContains(success_page, email_address)
|
||||
self.assertContains(success_page, "Cancel") # link to cancel invitation
|
||||
self.assertTrue(DomainInvitation.objects.filter(email=EMAIL).exists())
|
||||
self.assertTrue(DomainInvitation.objects.filter(email=email_address).exists())
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_domain_invitation_created_for_caps_email(self):
|
||||
"""Add user on a nonexistent email with CAPS creates an invitation to lowercase email.
|
||||
|
||||
Adding a non-existent user sends an email as a side-effect, so mock
|
||||
out the boto3 SES email sending here.
|
||||
"""
|
||||
# make sure there is no user with this email
|
||||
email_address = "mayor@igorville.gov"
|
||||
caps_email_address = "MAYOR@igorville.gov"
|
||||
User.objects.filter(email=email_address).delete()
|
||||
|
||||
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
|
||||
|
||||
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
add_page.form["email"] = caps_email_address
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
success_result = add_page.form.submit()
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
success_page = success_result.follow()
|
||||
|
||||
self.assertContains(success_page, email_address)
|
||||
self.assertContains(success_page, "Cancel") # link to cancel invitation
|
||||
self.assertTrue(DomainInvitation.objects.filter(email=email_address).exists())
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_domain_invitation_email_sent(self):
|
||||
"""Inviting a non-existent user sends them an email."""
|
||||
# make sure there is no user with this email
|
||||
EMAIL = "mayor@igorville.gov"
|
||||
User.objects.filter(email=EMAIL).delete()
|
||||
email_address = "mayor@igorville.gov"
|
||||
User.objects.filter(email=email_address).delete()
|
||||
|
||||
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
|
||||
|
||||
|
@ -1386,28 +1412,28 @@ class TestDomainManagers(TestDomainOverview):
|
|||
with boto3_mocking.clients.handler_for("sesv2", mock_client):
|
||||
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
add_page.form["email"] = EMAIL
|
||||
add_page.form["email"] = email_address
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
add_page.form.submit()
|
||||
# check the mock instance to see if `send_email` was called right
|
||||
mock_client_instance.send_email.assert_called_once_with(
|
||||
FromEmailAddress=settings.DEFAULT_FROM_EMAIL,
|
||||
Destination={"ToAddresses": [EMAIL]},
|
||||
Destination={"ToAddresses": [email_address]},
|
||||
Content=ANY,
|
||||
)
|
||||
|
||||
def test_domain_invitation_cancel(self):
|
||||
"""Posting to the delete view deletes an invitation."""
|
||||
EMAIL = "mayor@igorville.gov"
|
||||
invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=EMAIL)
|
||||
email_address = "mayor@igorville.gov"
|
||||
invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=email_address)
|
||||
self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id}))
|
||||
with self.assertRaises(DomainInvitation.DoesNotExist):
|
||||
DomainInvitation.objects.get(id=invitation.id)
|
||||
|
||||
def test_domain_invitation_cancel_no_permissions(self):
|
||||
"""Posting to the delete view as a different user should fail."""
|
||||
EMAIL = "mayor@igorville.gov"
|
||||
invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=EMAIL)
|
||||
email_address = "mayor@igorville.gov"
|
||||
invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=email_address)
|
||||
|
||||
other_user = User()
|
||||
other_user.save()
|
||||
|
@ -1419,20 +1445,20 @@ class TestDomainManagers(TestDomainOverview):
|
|||
@boto3_mocking.patching
|
||||
def test_domain_invitation_flow(self):
|
||||
"""Send an invitation to a new user, log in and load the dashboard."""
|
||||
EMAIL = "mayor@igorville.gov"
|
||||
User.objects.filter(email=EMAIL).delete()
|
||||
email_address = "mayor@igorville.gov"
|
||||
User.objects.filter(email=email_address).delete()
|
||||
|
||||
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
|
||||
|
||||
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
|
||||
|
||||
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||
add_page.form["email"] = EMAIL
|
||||
add_page.form["email"] = email_address
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
add_page.form.submit()
|
||||
|
||||
# user was invited, create them
|
||||
new_user = User.objects.create(username=EMAIL, email=EMAIL)
|
||||
new_user = User.objects.create(username=email_address, email=email_address)
|
||||
# log them in to `self.app`
|
||||
self.app.set_user(new_user.username)
|
||||
# and manually call the on each login callback
|
||||
|
@ -1941,19 +1967,19 @@ class TestDomainDNSSEC(TestDomainOverview):
|
|||
self.assertContains(updated_page, "Enable DNSSEC")
|
||||
|
||||
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"""
|
||||
|
||||
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")
|
||||
|
||||
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"""
|
||||
|
||||
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):
|
||||
"""When user clicks on save, a modal pops up."""
|
||||
|
@ -1974,7 +2000,7 @@ class TestDomainDNSSEC(TestDomainOverview):
|
|||
self.assertContains(response, "Trigger Disable DNSSEC Modal")
|
||||
|
||||
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.
|
||||
"""
|
||||
|
@ -1991,10 +2017,10 @@ class TestDomainDNSSEC(TestDomainOverview):
|
|||
)
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
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):
|
||||
"""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.
|
||||
"""
|
||||
|
@ -2017,7 +2043,7 @@ class TestDomainDNSSEC(TestDomainOverview):
|
|||
self.assertContains(result, "Digest is required", count=2, status_code=200)
|
||||
|
||||
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.
|
||||
"""
|
||||
|
@ -2040,7 +2066,7 @@ class TestDomainDNSSEC(TestDomainOverview):
|
|||
)
|
||||
|
||||
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.
|
||||
"""
|
||||
|
@ -2063,7 +2089,7 @@ class TestDomainDNSSEC(TestDomainOverview):
|
|||
)
|
||||
|
||||
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.
|
||||
"""
|
||||
|
@ -2086,7 +2112,7 @@ class TestDomainDNSSEC(TestDomainOverview):
|
|||
)
|
||||
|
||||
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.
|
||||
"""
|
||||
|
@ -2117,7 +2143,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
|||
|
||||
def test_application_status(self):
|
||||
"""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()
|
||||
|
||||
home_page = self.app.get("/")
|
||||
|
@ -2137,7 +2163,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
|||
self.user.status = "ineligible"
|
||||
self.user.save()
|
||||
|
||||
application = completed_application(status=DomainApplication.SUBMITTED, user=self.user)
|
||||
application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED, user=self.user)
|
||||
application.save()
|
||||
|
||||
home_page = self.app.get("/")
|
||||
|
@ -2152,7 +2178,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
|||
|
||||
def test_application_withdraw(self):
|
||||
"""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()
|
||||
|
||||
home_page = self.app.get("/")
|
||||
|
@ -2182,7 +2208,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
|||
|
||||
def test_application_status_no_permissions(self):
|
||||
"""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.save()
|
||||
application.creator = other_user
|
||||
|
@ -2202,7 +2228,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
|||
def test_approved_application_not_in_active_requests(self):
|
||||
"""An approved application is not shown in the Active
|
||||
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()
|
||||
|
||||
home_page = self.app.get("/")
|
||||
|
|
|
@ -293,9 +293,9 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
|||
return self.pending_applications()
|
||||
|
||||
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(
|
||||
creator=self.request.user, status=DomainApplication.APPROVED
|
||||
creator=self.request.user, status=DomainApplication.ApplicationStatus.APPROVED
|
||||
).count()
|
||||
return approved_application_count > 0
|
||||
|
||||
|
@ -308,11 +308,15 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
|||
|
||||
def pending_applications(self):
|
||||
"""Returns a List of user's applications with one of the following states:
|
||||
SUBMITTED, IN_REVIEW, ACTION_NEEDED"""
|
||||
# if the current application has ACTION_NEEDED status, this check should not be performed
|
||||
if self.application.status == DomainApplication.ACTION_NEEDED:
|
||||
ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW, ApplicationStatus.ACTION_NEEDED"""
|
||||
# if the current application has ApplicationStatus.ACTION_NEEDED status, this check should not be performed
|
||||
if self.application.status == DomainApplication.ApplicationStatus.ACTION_NEEDED:
|
||||
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)
|
||||
|
||||
def get_context_data(self):
|
||||
|
|
|
@ -434,7 +434,7 @@ class DomainDsDataView(DomainFormBaseView):
|
|||
return initial_data
|
||||
|
||||
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})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
@ -473,7 +473,7 @@ class DomainDsDataView(DomainFormBaseView):
|
|||
modal_button = (
|
||||
'<button type="submit" '
|
||||
'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
|
||||
|
@ -523,7 +523,7 @@ class DomainDsDataView(DomainFormBaseView):
|
|||
logger.error(f"Registry error: {err}")
|
||||
return self.form_invalid(formset)
|
||||
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
|
||||
return super().form_valid(formset)
|
||||
|
||||
|
|
|
@ -101,10 +101,10 @@ class DomainPermission(PermissionsLoginMixin):
|
|||
|
||||
# Analysts may manage domains, when they are in these statuses:
|
||||
valid_domain_statuses = [
|
||||
DomainApplication.APPROVED,
|
||||
DomainApplication.IN_REVIEW,
|
||||
DomainApplication.REJECTED,
|
||||
DomainApplication.ACTION_NEEDED,
|
||||
DomainApplication.ApplicationStatus.APPROVED,
|
||||
DomainApplication.ApplicationStatus.IN_REVIEW,
|
||||
DomainApplication.ApplicationStatus.REJECTED,
|
||||
DomainApplication.ApplicationStatus.ACTION_NEEDED,
|
||||
# Edge case - some domains do not have
|
||||
# a status or DomainInformation... aka a status of 'None'.
|
||||
# It is necessary to access those to correct errors.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue