Merge branch 'main' into za/1241-imported-ao-data

This commit is contained in:
zandercymatics 2023-11-13 11:22:43 -07:00
commit 91019ee9b3
No known key found for this signature in database
GPG key ID: FF4636ABEC9682B7
83 changed files with 819 additions and 2013 deletions

View file

@ -136,7 +136,7 @@ class DomainInvitation {
-- --
} }
DomainInvitation -- Domain DomainInvitation -- Domain
DomainInvitation .[#green].> UserDomainRole : User.first_login() DomainInvitation .[#green].> UserDomainRole : User.on_each_login()
actor applicant #Red actor applicant #Red
applicant -d-> DomainApplication : **/register** applicant -d-> DomainApplication : **/register**

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Before After
Before After

View file

@ -246,7 +246,7 @@ This will allow Docker to mount the files to a container (under `/app`) for our
*You are now ready to run migration scripts.* *You are now ready to run migration scripts.*
## Transition Domains (Part 2) - Running the Migration Scripts ## Transition Domains (Part 2) - Running the Migration Scripts
While keeping the same ssh instance open (if you are running on a sandbox), run through the following commands. If you run into the error that. If you cannot run `manage.py` commands, try running `/tmp/lifecycle/shell` in the ssh instance. While keeping the same ssh instance open (if you are running on a sandbox), run through the following commands.If you cannot run `manage.py` commands, try running `/tmp/lifecycle/shell` in the ssh instance.
### STEP 1: Load Transition Domains ### STEP 1: Load Transition Domains
@ -274,8 +274,6 @@ Directs the script to load only the first 100 entries into the table. You can a
This will delete all the data in transtion_domain. It is helpful if you want to see the entries reload from scratch or for clearing test data. This will delete all the data in transtion_domain. It is helpful if you want to see the entries reload from scratch or for clearing test data.
###### (arguments that override filepaths and directories if needed) ###### (arguments that override filepaths and directories if needed)
`--infer_filenames`
Determines if we should infer filenames or not. Recommended to be enabled only in a development or testing setting..
`--directory` `--directory`
Defines the directory where all data files and the JSON are stored. Defines the directory where all data files and the JSON are stored.
@ -307,7 +305,8 @@ Defines the filename for domain type adhocs.
`--authority_adhoc_filename` `--authority_adhoc_filename`
Defines the filename for domain type adhocs. Defines the filename for domain type adhocs.
`--infer_filenames`
Determines if we should infer filenames or not. This setting is not available for use in environments with the flag `settings.DEBUG` set to false, as it is intended for local development only.
### STEP 2: Transfer Transition Domain data into main Domain tables ### STEP 2: Transfer Transition Domain data into main Domain tables

View file

@ -1,5 +1,5 @@
[flake8] [flake8]
max-line-length = 88 max-line-length = 120
max-complexity = 10 max-complexity = 10
extend-ignore = E203 extend-ignore = E203
per-file-ignores = __init__.py:F401,F403,E402 per-file-ignores = __init__.py:F401,F403,E402

View file

@ -10,9 +10,7 @@ from login_required import login_not_required
from cachetools.func import ttl_cache from cachetools.func import ttl_cache
DOMAIN_FILE_URL = ( DOMAIN_FILE_URL = "https://raw.githubusercontent.com/cisagov/dotgov-data/main/current-full.csv"
"https://raw.githubusercontent.com/cisagov/dotgov-data/main/current-full.csv"
)
DOMAIN_API_MESSAGES = { DOMAIN_API_MESSAGES = {
@ -22,8 +20,7 @@ DOMAIN_API_MESSAGES = {
"extra_dots": "Enter the .gov domain you want without any periods.", "extra_dots": "Enter the .gov domain you want without any periods.",
"unavailable": "That domain isnt available. Try entering another one." "unavailable": "That domain isnt available. Try entering another one."
" Contact us if you need help coming up with a domain.", " Contact us if you need help coming up with a domain.",
"invalid": "Enter a domain using only letters," "invalid": "Enter a domain using only letters, numbers, or hyphens (though we don't recommend using hyphens).",
" numbers, or hyphens (though we don't recommend using hyphens).",
"success": "That domain is available!", "success": "That domain is available!",
"error": "Error finding domain availability.", "error": "Error finding domain availability.",
} }
@ -82,24 +79,13 @@ def available(request, domain=""):
DraftDomain = apps.get_model("registrar.DraftDomain") DraftDomain = apps.get_model("registrar.DraftDomain")
# validate that the given domain could be a domain name and fail early if # validate that the given domain could be a domain name and fail early if
# not. # not.
if not ( if not (DraftDomain.string_could_be_domain(domain) or DraftDomain.string_could_be_domain(domain + ".gov")):
DraftDomain.string_could_be_domain(domain) return JsonResponse({"available": False, "message": DOMAIN_API_MESSAGES["invalid"]})
or DraftDomain.string_could_be_domain(domain + ".gov")
):
return JsonResponse(
{"available": False, "message": DOMAIN_API_MESSAGES["invalid"]}
)
# a domain is available if it is NOT in the list of current domains # a domain is available if it is NOT in the list of current domains
try: try:
if check_domain_available(domain): if check_domain_available(domain):
return JsonResponse( return JsonResponse({"available": True, "message": DOMAIN_API_MESSAGES["success"]})
{"available": True, "message": DOMAIN_API_MESSAGES["success"]}
)
else: else:
return JsonResponse( return JsonResponse({"available": False, "message": DOMAIN_API_MESSAGES["unavailable"]})
{"available": False, "message": DOMAIN_API_MESSAGES["unavailable"]}
)
except Exception: except Exception:
return JsonResponse( return JsonResponse({"available": False, "message": DOMAIN_API_MESSAGES["error"]})
{"available": False, "message": DOMAIN_API_MESSAGES["error"]}
)

View file

@ -49,13 +49,13 @@ class OpenIdConnectBackend(ModelBackend):
user, created = UserModel.objects.update_or_create(**args) user, created = UserModel.objects.update_or_create(**args)
if created: if created:
user = self.configure_user(user, **kwargs) user = self.configure_user(user, **kwargs)
# run a newly created user's callback for a first-time login
user.first_login()
else: else:
try: try:
user = UserModel.objects.get_by_natural_key(username) user = UserModel.objects.get_by_natural_key(username)
except UserModel.DoesNotExist: except UserModel.DoesNotExist:
return None return None
# run this callback for a each login
user.on_each_login()
return user return user
def clean_username(self, username): def clean_username(self, username):

View file

@ -72,9 +72,7 @@ class Client(oic.Client):
try: try:
# discover and store the provider (OP) urls, etc # discover and store the provider (OP) urls, etc
self.provider_config(provider["srv_discovery_url"]) self.provider_config(provider["srv_discovery_url"])
self.store_registration_info( self.store_registration_info(RegistrationResponse(**provider["client_registration"]))
RegistrationResponse(**provider["client_registration"])
)
except Exception as err: except Exception as err:
logger.error(err) logger.error(err)
logger.error( logger.error(
@ -169,9 +167,7 @@ class Client(oic.Client):
if isinstance(authn_response, ErrorResponse): if isinstance(authn_response, ErrorResponse):
error = authn_response.get("error", "") error = authn_response.get("error", "")
if error == "login_required": if error == "login_required":
logger.warning( logger.warning("User was not logged in (%s), trying again for %s" % (error, state))
"User was not logged in (%s), trying again for %s" % (error, state)
)
return self.create_authn_request(session) return self.create_authn_request(session)
else: else:
logger.error("Unable to process response %s for %s" % (error, state)) logger.error("Unable to process response %s for %s" % (error, state))
@ -190,9 +186,7 @@ class Client(oic.Client):
if self.behaviour.get("response_type") == "code": if self.behaviour.get("response_type") == "code":
# need an access token to get user info (and to log the user out later) # need an access token to get user info (and to log the user out later)
self._request_token( self._request_token(authn_response["state"], authn_response["code"], session)
authn_response["state"], authn_response["code"], session
)
user_info = self._get_user_info(state, session) user_info = self._get_user_info(state, session)
@ -216,10 +210,7 @@ class Client(oic.Client):
# ErrorResponse is not raised, it is passed back... # ErrorResponse is not raised, it is passed back...
if isinstance(info_response, ErrorResponse): if isinstance(info_response, ErrorResponse):
logger.error( logger.error("Unable to get user info (%s) for %s" % (info_response.get("error", ""), state))
"Unable to get user info (%s) for %s"
% (info_response.get("error", ""), state)
)
raise o_e.AuthenticationFailed(locator=state) raise o_e.AuthenticationFailed(locator=state)
logger.debug("user info: %s" % info_response) logger.debug("user info: %s" % info_response)
@ -249,10 +240,7 @@ class Client(oic.Client):
# ErrorResponse is not raised, it is passed back... # ErrorResponse is not raised, it is passed back...
if isinstance(token_response, ErrorResponse): if isinstance(token_response, ErrorResponse):
logger.error( logger.error("Unable to get token (%s) for %s" % (token_response.get("error", ""), state))
"Unable to get token (%s) for %s"
% (token_response.get("error", ""), state)
)
raise o_e.AuthenticationFailed(locator=state) raise o_e.AuthenticationFailed(locator=state)
logger.debug("token response %s" % token_response) logger.debug("token response %s" % token_response)

View file

@ -85,12 +85,8 @@ class ViewsTest(TestCase):
session.save() session.save()
# mock # mock
mock_client.callback.side_effect = self.user_info mock_client.callback.side_effect = self.user_info
mock_client.registration_response = { mock_client.registration_response = {"post_logout_redirect_uris": ["http://example.com/back"]}
"post_logout_redirect_uris": ["http://example.com/back"] mock_client.provider_info = {"end_session_endpoint": "http://example.com/log_me_out"}
}
mock_client.provider_info = {
"end_session_endpoint": "http://example.com/log_me_out"
}
mock_client.client_id = "TEST" mock_client.client_id = "TEST"
# test # test
with less_console_noise(): with less_console_noise():

View file

@ -92,11 +92,7 @@ def logout(request, next_page=None):
and len(CLIENT.registration_response["post_logout_redirect_uris"]) > 0 and len(CLIENT.registration_response["post_logout_redirect_uris"]) > 0
): ):
request_args.update( request_args.update(
{ {"post_logout_redirect_uri": CLIENT.registration_response["post_logout_redirect_uris"][0]}
"post_logout_redirect_uri": CLIENT.registration_response[
"post_logout_redirect_uris"
][0]
}
) )
url = CLIENT.provider_info["end_session_endpoint"] url = CLIENT.provider_info["end_session_endpoint"]

View file

@ -29,6 +29,8 @@ services:
- IS_PRODUCTION=False - IS_PRODUCTION=False
# Tell Django where it is being hosted # Tell Django where it is being hosted
- DJANGO_BASE_URL=http://localhost:8080 - DJANGO_BASE_URL=http://localhost:8080
# Is this a production environment
- IS_PRODUCTION
# Public site URL link # Public site URL link
- GETGOV_PUBLIC_SITE_URL=https://beta.get.gov - GETGOV_PUBLIC_SITE_URL=https://beta.get.gov
# Set a username for accessing the registry # Set a username for accessing the registry

View file

@ -95,9 +95,7 @@ class EPPLibWrapper:
try: try:
if not self.pool_status.connection_success: if not self.pool_status.connection_success:
raise LoginError( raise LoginError("Couldn't connect to the registry after three attempts")
"Couldn't connect to the registry after three attempts"
)
with self._pool.get() as connection: with self._pool.get() as connection:
response = connection.send(command) response = connection.send(command)
except Timeout as t: except Timeout as t:
@ -239,6 +237,4 @@ try:
logger.info("registry client initialized") logger.info("registry client initialized")
except Exception: except Exception:
CLIENT = None # type: ignore CLIENT = None # type: ignore
logger.warning( logger.warning("Unable to configure epplib. Registrar cannot contact registry.", exc_info=True)
"Unable to configure epplib. Registrar cannot contact registry.", exc_info=True
)

View file

@ -103,9 +103,7 @@ class TestConnectionPool(TestCase):
], ],
cl_id="gov2023-ote", cl_id="gov2023-ote",
cr_id="gov2023-ote", cr_id="gov2023-ote",
cr_date=datetime.datetime( cr_date=datetime.datetime(2023, 8, 15, 23, 56, 36, tzinfo=tzlocal()),
2023, 8, 15, 23, 56, 36, tzinfo=tzlocal()
),
up_id="gov2023-ote", up_id="gov2023-ote",
up_date=datetime.datetime(2023, 8, 17, 2, 3, 19, tzinfo=tzlocal()), up_date=datetime.datetime(2023, 8, 17, 2, 3, 19, tzinfo=tzlocal()),
tr_date=None, tr_date=None,
@ -129,9 +127,7 @@ class TestConnectionPool(TestCase):
# Mock what happens inside the "with" # Mock what happens inside the "with"
with ExitStack() as stack: with ExitStack() as stack:
stack.enter_context( stack.enter_context(patch.object(EPPConnectionPool, "_create_socket", self.fake_socket))
patch.object(EPPConnectionPool, "_create_socket", self.fake_socket)
)
stack.enter_context(patch.object(Socket, "connect", self.fake_client)) stack.enter_context(patch.object(Socket, "connect", self.fake_client))
stack.enter_context(patch.object(SocketTransport, "send", self.fake_send)) stack.enter_context(patch.object(SocketTransport, "send", self.fake_send))
stack.enter_context(patch.object(SocketTransport, "receive", fake_receive)) stack.enter_context(patch.object(SocketTransport, "receive", fake_receive))
@ -176,9 +172,7 @@ class TestConnectionPool(TestCase):
], ],
cl_id="gov2023-ote", cl_id="gov2023-ote",
cr_id="gov2023-ote", cr_id="gov2023-ote",
cr_date=datetime.datetime( cr_date=datetime.datetime(2023, 8, 15, 23, 56, 36, tzinfo=tzlocal()),
2023, 8, 15, 23, 56, 36, tzinfo=tzlocal()
),
up_id="gov2023-ote", up_id="gov2023-ote",
up_date=datetime.datetime(2023, 8, 17, 2, 3, 19, tzinfo=tzlocal()), up_date=datetime.datetime(2023, 8, 17, 2, 3, 19, tzinfo=tzlocal()),
tr_date=None, tr_date=None,
@ -202,9 +196,7 @@ class TestConnectionPool(TestCase):
# Mock what happens inside the "with" # Mock what happens inside the "with"
with ExitStack() as stack: with ExitStack() as stack:
stack.enter_context( stack.enter_context(patch.object(EPPConnectionPool, "_create_socket", self.fake_socket))
patch.object(EPPConnectionPool, "_create_socket", self.fake_socket)
)
stack.enter_context(patch.object(Socket, "connect", self.fake_client)) stack.enter_context(patch.object(Socket, "connect", self.fake_client))
stack.enter_context(patch.object(SocketTransport, "send", self.fake_send)) stack.enter_context(patch.object(SocketTransport, "send", self.fake_send))
stack.enter_context(patch.object(SocketTransport, "receive", fake_receive)) stack.enter_context(patch.object(SocketTransport, "receive", fake_receive))
@ -218,9 +210,7 @@ class TestConnectionPool(TestCase):
# that they cannot connect to EPP # that they cannot connect to EPP
with self.assertRaises(RegistryError): with self.assertRaises(RegistryError):
expected = "InfoDomain failed to execute due to a connection error." expected = "InfoDomain failed to execute due to a connection error."
result = registry.send( result = registry.send(commands.InfoDomain(name="test.gov"), cleaned=True)
commands.InfoDomain(name="test.gov"), cleaned=True
)
self.assertEqual(result, expected) self.assertEqual(result, expected)
# A subsequent command should be successful, as the pool restarts # A subsequent command should be successful, as the pool restarts
@ -240,9 +230,7 @@ class TestConnectionPool(TestCase):
right as we send a command.""" right as we send a command."""
with ExitStack() as stack: with ExitStack() as stack:
stack.enter_context( stack.enter_context(patch.object(EPPConnectionPool, "_create_socket", self.fake_socket))
patch.object(EPPConnectionPool, "_create_socket", self.fake_socket)
)
stack.enter_context(patch.object(Socket, "connect", self.fake_client)) stack.enter_context(patch.object(Socket, "connect", self.fake_client))
# Pool should be running # Pool should be running
@ -252,7 +240,5 @@ class TestConnectionPool(TestCase):
# Try to send a command out - should fail # Try to send a command out - should fail
with self.assertRaises(RegistryError): with self.assertRaises(RegistryError):
expected = "InfoDomain failed to execute due to a connection error." expected = "InfoDomain failed to execute due to a connection error."
result = registry.send( result = registry.send(commands.InfoDomain(name="test.gov"), cleaned=True)
commands.InfoDomain(name="test.gov"), cleaned=True
)
self.assertEqual(result, expected) self.assertEqual(result, expected)

View file

@ -125,9 +125,7 @@ class EPPConnectionPool(ConnectionPool):
# Open multiple connections # Open multiple connections
for i in range(self.size): for i in range(self.size):
self.greenlets.append( self.greenlets.append(gevent.spawn_later(self.spawn_frequency * i, self._addOne))
gevent.spawn_later(self.spawn_frequency * i, self._addOne)
)
# Open a "keepalive" thread if we want to ping open connections # Open a "keepalive" thread if we want to ping open connections
if self.keepalive: if self.keepalive:

View file

@ -28,14 +28,8 @@ class PoolError(Exception):
# Used variables due to linter requirements # Used variables due to linter requirements
kill_failed = "Could not kill all connections. Are multiple pools running?" kill_failed = "Could not kill all connections. Are multiple pools running?"
conn_failed = ( conn_failed = "Failed to execute due to a registry error. See previous logs to determine the cause of the error."
"Failed to execute due to a registry error." alive_failed = "Failed to keep the connection alive. It is likely that the registry returned a LoginError."
" See previous logs to determine the cause of the error."
)
alive_failed = (
"Failed to keep the connection alive. "
"It is likely that the registry returned a LoginError."
)
_error_mapping = { _error_mapping = {
PoolErrorCodes.KILL_ALL_FAILED: kill_failed, PoolErrorCodes.KILL_ALL_FAILED: kill_failed,
PoolErrorCodes.NEW_CONNECTION_FAILED: conn_failed, PoolErrorCodes.NEW_CONNECTION_FAILED: conn_failed,

View file

@ -1,5 +1,5 @@
[tool.black] [tool.black]
line-length=88 line-length=120
[tool.mypy] [tool.mypy]
ignore_missing_imports = true ignore_missing_imports = true

View file

@ -73,9 +73,7 @@ class ListHeaderAdmin(AuditedAdmin):
filters = self.get_filters(request) filters = self.get_filters(request)
# Pass the filtered values to the template context # Pass the filtered values to the template context
extra_context["filters"] = filters extra_context["filters"] = filters
extra_context["search_query"] = request.GET.get( extra_context["search_query"] = request.GET.get("q", "") # Assuming the search query parameter is 'q'
"q", ""
) # Assuming the search query parameter is 'q'
return super().changelist_view(request, extra_context=extra_context) return super().changelist_view(request, extra_context=extra_context)
def get_filters(self, request): def get_filters(self, request):
@ -91,11 +89,7 @@ class ListHeaderAdmin(AuditedAdmin):
for param in request.GET.keys(): for param in request.GET.keys():
# Exclude the default search parameter 'q' # Exclude the default search parameter 'q'
if param != "q" and param != "o": if param != "q" and param != "o":
parameter_name = ( parameter_name = param.replace("__exact", "").replace("_type", "").replace("__id", " id")
param.replace("__exact", "")
.replace("_type", "")
.replace("__id", " id")
)
if parameter_name == "investigator id": if parameter_name == "investigator id":
# Retrieves the corresponding contact from Users # Retrieves the corresponding contact from Users
@ -613,8 +607,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
messages.error( messages.error(
request, request,
"This action is not permitted. The domain " "This action is not permitted. The domain is already active.",
+ "is already active.",
) )
else: else:
@ -627,9 +620,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
models.DomainApplication.APPROVED: obj.approve, models.DomainApplication.APPROVED: obj.approve,
models.DomainApplication.WITHDRAWN: obj.withdraw, models.DomainApplication.WITHDRAWN: obj.withdraw,
models.DomainApplication.REJECTED: obj.reject, models.DomainApplication.REJECTED: obj.reject,
models.DomainApplication.INELIGIBLE: ( models.DomainApplication.INELIGIBLE: (obj.reject_with_prejudice),
obj.reject_with_prejudice
),
} }
selected_method = status_method_mapping.get(obj.status) selected_method = status_method_mapping.get(obj.status)
if selected_method is None: if selected_method is None:
@ -649,8 +640,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
messages.error( messages.error(
request, request,
"This action is not permitted for applications " "This action is not permitted for applications with a restricted creator.",
+ "with a restricted creator.",
) )
def get_readonly_fields(self, request, obj=None): def get_readonly_fields(self, request, obj=None):
@ -669,9 +659,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
readonly_fields.extend([field.name for field in self.model._meta.fields]) readonly_fields.extend([field.name for field in self.model._meta.fields])
# Add the multi-select fields to readonly_fields: # Add the multi-select fields to readonly_fields:
# Complex fields like ManyToManyField require special handling # Complex fields like ManyToManyField require special handling
readonly_fields.extend( readonly_fields.extend(["current_websites", "other_contacts", "alternative_domains"])
["current_websites", "other_contacts", "alternative_domains"]
)
if request.user.has_perm("registrar.full_access_permission"): if request.user.has_perm("registrar.full_access_permission"):
return readonly_fields return readonly_fields
@ -739,9 +727,7 @@ class DomainAdmin(ListHeaderAdmin):
def organization_type(self, obj): def organization_type(self, obj):
return obj.domain_info.get_organization_type_display() return obj.domain_info.get_organization_type_display()
organization_type.admin_order_field = ( # type: ignore organization_type.admin_order_field = "domain_info__organization_type" # type: ignore
"domain_info__organization_type"
)
# Filters # Filters
list_filter = ["domain_info__organization_type", "state"] list_filter = ["domain_info__organization_type", "state"]
@ -846,9 +832,7 @@ class DomainAdmin(ListHeaderAdmin):
if not err.is_connection_error(): if not err.is_connection_error():
# If nothing is found, will default to returned err # If nothing is found, will default to returned err
message = error_messages.get(err.code, err) message = error_messages.get(err.code, err)
self.message_user( self.message_user(request, f"Error deleting this Domain: {message}", messages.ERROR)
request, f"Error deleting this Domain: {message}", messages.ERROR
)
except TransitionNotAllowed: except TransitionNotAllowed:
if obj.state == Domain.State.DELETED: if obj.state == Domain.State.DELETED:
self.message_user( self.message_user(
@ -886,8 +870,7 @@ class DomainAdmin(ListHeaderAdmin):
else: else:
self.message_user( self.message_user(
request, request,
f"The registry statuses are {statuses}. " f"The registry statuses are {statuses}. These statuses are from the provider of the .gov registry.",
"These statuses are from the provider of the .gov registry.",
) )
return HttpResponseRedirect(".") return HttpResponseRedirect(".")
@ -916,11 +899,7 @@ class DomainAdmin(ListHeaderAdmin):
else: else:
self.message_user( self.message_user(
request, request,
( ("%s is in client hold. This domain is no longer accessible on the public internet.") % obj.name,
"%s is in client hold. This domain is no longer accessible on"
" the public internet."
)
% obj.name,
) )
return HttpResponseRedirect(".") return HttpResponseRedirect(".")
@ -949,8 +928,7 @@ class DomainAdmin(ListHeaderAdmin):
else: else:
self.message_user( self.message_user(
request, request,
("%s is ready. This domain is accessible on the public internet.") ("%s is ready. This domain is accessible on the public internet.") % obj.name,
% obj.name,
) )
return HttpResponseRedirect(".") return HttpResponseRedirect(".")
@ -973,9 +951,9 @@ class DomainAdmin(ListHeaderAdmin):
# Fixes a bug wherein users which are only is_staff # Fixes a bug wherein users which are only is_staff
# can access 'change' when GET, # can access 'change' when GET,
# but cannot access this page when it is a request of type POST. # but cannot access this page when it is a request of type POST.
if request.user.has_perm( if request.user.has_perm("registrar.full_access_permission") or request.user.has_perm(
"registrar.full_access_permission" "registrar.analyst_access_permission"
) or request.user.has_perm("registrar.analyst_access_permission"): ):
return True return True
return super().has_change_permission(request, obj) return super().has_change_permission(request, obj)

View file

@ -161,6 +161,26 @@ h1, h2, h3 {
font-size: 14px; font-size: 14px;
} }
// right justify custom buttons and display as links
.submit-row input.custom-link-button,
.submit-row input.custom-link-button:hover {
background: none;
border: none;
color: var(--link-fg);
cursor: pointer;
text-decoration: none;
padding: 0;
font-size: inherit;
margin-left: auto;
}
.submit-row div.spacer {
flex-grow: 1;
}
.submit-row span {
margin-top: units(1);
}
// Customize
// Keep th from collapsing // Keep th from collapsing
.min-width-25 { .min-width-25 {
min-width: 25px; min-width: 25px;

View file

@ -212,6 +212,7 @@ TEMPLATES = [
"registrar.context_processors.language_code", "registrar.context_processors.language_code",
"registrar.context_processors.canonical_path", "registrar.context_processors.canonical_path",
"registrar.context_processors.is_demo_site", "registrar.context_processors.is_demo_site",
"registrar.context_processors.is_production",
], ],
}, },
}, },
@ -528,9 +529,7 @@ OIDC_PROVIDERS = {
"acr_value": "http://idmanagement.gov/ns/assurance/ial/2", "acr_value": "http://idmanagement.gov/ns/assurance/ial/2",
}, },
"client_registration": { "client_registration": {
"client_id": ( "client_id": ("urn:gov:cisa:openidconnect.profiles:sp:sso:cisa:dotgov_registrar"),
"urn:gov:cisa:openidconnect.profiles:sp:sso:cisa:dotgov_registrar"
),
"redirect_uris": [f"{env_base_url}/openid/callback/login/"], "redirect_uris": [f"{env_base_url}/openid/callback/login/"],
"post_logout_redirect_uris": [f"{env_base_url}/openid/callback/logout/"], "post_logout_redirect_uris": [f"{env_base_url}/openid/callback/logout/"],
"token_endpoint_auth_method": ["private_key_jwt"], "token_endpoint_auth_method": ["private_key_jwt"],

View file

@ -31,3 +31,8 @@ def is_demo_site(request):
should not appear. should not appear.
""" """
return {"IS_DEMO_SITE": settings.IS_DEMO_SITE} return {"IS_DEMO_SITE": settings.IS_DEMO_SITE}
def is_production(request):
"""Add a boolean if this is our production site."""
return {"IS_PRODUCTION": settings.IS_PRODUCTION}

View file

@ -97,9 +97,7 @@ class DomainApplicationFixture:
def _set_non_foreign_key_fields(cls, da: DomainApplication, app: dict): def _set_non_foreign_key_fields(cls, da: DomainApplication, app: dict):
"""Helper method used by `load`.""" """Helper method used by `load`."""
da.status = app["status"] if "status" in app else "started" da.status = app["status"] if "status" in app else "started"
da.organization_type = ( da.organization_type = app["organization_type"] if "organization_type" in app else "federal"
app["organization_type"] if "organization_type" in app else "federal"
)
da.federal_agency = ( da.federal_agency = (
app["federal_agency"] app["federal_agency"]
if "federal_agency" in app if "federal_agency" in app
@ -112,40 +110,25 @@ class DomainApplicationFixture:
if "federal_type" in app if "federal_type" in app
else random.choice(["executive", "judicial", "legislative"]) # nosec else random.choice(["executive", "judicial", "legislative"]) # nosec
) )
da.address_line1 = ( da.address_line1 = app["address_line1"] if "address_line1" in app else fake.street_address()
app["address_line1"] if "address_line1" in app else fake.street_address()
)
da.address_line2 = app["address_line2"] if "address_line2" in app else None da.address_line2 = app["address_line2"] if "address_line2" in app else None
da.city = app["city"] if "city" in app else fake.city() da.city = app["city"] if "city" in app else fake.city()
da.state_territory = ( da.state_territory = app["state_territory"] if "state_territory" in app else fake.state_abbr()
app["state_territory"] if "state_territory" in app else fake.state_abbr()
)
da.zipcode = app["zipcode"] if "zipcode" in app else fake.postalcode() da.zipcode = app["zipcode"] if "zipcode" in app else fake.postalcode()
da.urbanization = app["urbanization"] if "urbanization" in app else None da.urbanization = app["urbanization"] if "urbanization" in app else None
da.purpose = app["purpose"] if "purpose" in app else fake.paragraph() da.purpose = app["purpose"] if "purpose" in app else fake.paragraph()
da.anything_else = app["anything_else"] if "anything_else" in app else None da.anything_else = app["anything_else"] if "anything_else" in app else None
da.is_policy_acknowledged = ( da.is_policy_acknowledged = app["is_policy_acknowledged"] if "is_policy_acknowledged" in app else True
app["is_policy_acknowledged"] if "is_policy_acknowledged" in app else True
)
@classmethod @classmethod
def _set_foreign_key_fields(cls, da: DomainApplication, app: dict, user: User): def _set_foreign_key_fields(cls, da: DomainApplication, app: dict, user: User):
"""Helper method used by `load`.""" """Helper method used by `load`."""
if not da.investigator: if not da.investigator:
da.investigator = ( da.investigator = User.objects.get(username=user.username) if "investigator" in app else None
User.objects.get(username=user.username)
if "investigator" in app
else None
)
if not da.authorizing_official: if not da.authorizing_official:
if ( if "authorizing_official" in app and app["authorizing_official"] is not None:
"authorizing_official" in app da.authorizing_official, _ = Contact.objects.get_or_create(**app["authorizing_official"])
and app["authorizing_official"] is not None
):
da.authorizing_official, _ = Contact.objects.get_or_create(
**app["authorizing_official"]
)
else: else:
da.authorizing_official = Contact.objects.create(**cls.fake_contact()) da.authorizing_official = Contact.objects.create(**cls.fake_contact())
@ -157,13 +140,9 @@ class DomainApplicationFixture:
if not da.requested_domain: if not da.requested_domain:
if "requested_domain" in app and app["requested_domain"] is not None: if "requested_domain" in app and app["requested_domain"] is not None:
da.requested_domain, _ = DraftDomain.objects.get_or_create( da.requested_domain, _ = DraftDomain.objects.get_or_create(name=app["requested_domain"])
name=app["requested_domain"]
)
else: else:
da.requested_domain = DraftDomain.objects.create( da.requested_domain = DraftDomain.objects.create(name=cls.fake_dot_gov())
name=cls.fake_dot_gov()
)
@classmethod @classmethod
def _set_many_to_many_relations(cls, da: DomainApplication, app: dict): def _set_many_to_many_relations(cls, da: DomainApplication, app: dict):
@ -173,32 +152,25 @@ class DomainApplicationFixture:
da.other_contacts.add(Contact.objects.get_or_create(**contact)[0]) da.other_contacts.add(Contact.objects.get_or_create(**contact)[0])
elif not da.other_contacts.exists(): elif not da.other_contacts.exists():
other_contacts = [ other_contacts = [
Contact.objects.create(**cls.fake_contact()) Contact.objects.create(**cls.fake_contact()) for _ in range(random.randint(0, 3)) # nosec
for _ in range(random.randint(0, 3)) # nosec
] ]
da.other_contacts.add(*other_contacts) da.other_contacts.add(*other_contacts)
if "current_websites" in app: if "current_websites" in app:
for website in app["current_websites"]: for website in app["current_websites"]:
da.current_websites.add( da.current_websites.add(Website.objects.get_or_create(website=website)[0])
Website.objects.get_or_create(website=website)[0]
)
elif not da.current_websites.exists(): elif not da.current_websites.exists():
current_websites = [ current_websites = [
Website.objects.create(website=fake.uri()) Website.objects.create(website=fake.uri()) for _ in range(random.randint(0, 3)) # nosec
for _ in range(random.randint(0, 3)) # nosec
] ]
da.current_websites.add(*current_websites) da.current_websites.add(*current_websites)
if "alternative_domains" in app: if "alternative_domains" in app:
for domain in app["alternative_domains"]: for domain in app["alternative_domains"]:
da.alternative_domains.add( da.alternative_domains.add(Website.objects.get_or_create(website=domain)[0])
Website.objects.get_or_create(website=domain)[0]
)
elif not da.alternative_domains.exists(): elif not da.alternative_domains.exists():
alternative_domains = [ alternative_domains = [
Website.objects.create(website=cls.fake_dot_gov()) Website.objects.create(website=cls.fake_dot_gov()) for _ in range(random.randint(0, 3)) # nosec
for _ in range(random.randint(0, 3)) # nosec
] ]
da.alternative_domains.add(*alternative_domains) da.alternative_domains.add(*alternative_domains)
@ -242,9 +214,7 @@ class DomainFixture(DomainApplicationFixture):
for user in users: for user in users:
# approve one of each users in review status domains # approve one of each users in review status domains
application = DomainApplication.objects.filter( application = DomainApplication.objects.filter(creator=user, status=DomainApplication.IN_REVIEW).last()
creator=user, status=DomainApplication.IN_REVIEW
).last()
logger.debug(f"Approving {application} for {user}") logger.debug(f"Approving {application} for {user}")
application.approve() application.approve()
application.save() application.save()

View file

@ -50,9 +50,7 @@ class RegistrarForm(forms.Form):
"""Returns a dict of form field values gotten from `obj`.""" """Returns a dict of form field values gotten from `obj`."""
if obj is None: if obj is None:
return {} return {}
return { return {name: getattr(obj, name) for name in cls.declared_fields.keys()} # type: ignore
name: getattr(obj, name) for name in cls.declared_fields.keys()
} # type: ignore
class RegistrarFormSet(forms.BaseFormSet): class RegistrarFormSet(forms.BaseFormSet):
@ -178,10 +176,7 @@ class TribalGovernmentForm(RegistrarForm):
def clean(self): def clean(self):
"""Needs to be either state or federally recognized.""" """Needs to be either state or federally recognized."""
if not ( if not (self.cleaned_data["federally_recognized_tribe"] or self.cleaned_data["state_recognized_tribe"]):
self.cleaned_data["federally_recognized_tribe"]
or self.cleaned_data["state_recognized_tribe"]
):
raise forms.ValidationError( raise forms.ValidationError(
# no sec because we are using it to include an internal URL # no sec because we are using it to include an internal URL
# into a link. There should be no user-facing input in the # into a link. There should be no user-facing input in the
@ -203,11 +198,7 @@ class OrganizationFederalForm(RegistrarForm):
federal_type = forms.ChoiceField( federal_type = forms.ChoiceField(
choices=DomainApplication.BranchChoices.choices, choices=DomainApplication.BranchChoices.choices,
widget=forms.RadioSelect, widget=forms.RadioSelect,
error_messages={ error_messages={"required": ("Select the part of the federal government your organization is in.")},
"required": (
"Select the part of the federal government your organization is in."
)
},
) )
@ -227,10 +218,7 @@ class OrganizationElectionForm(RegistrarForm):
is_election_board = self.cleaned_data["is_election_board"] is_election_board = self.cleaned_data["is_election_board"]
if is_election_board is None: if is_election_board is None:
raise forms.ValidationError( raise forms.ValidationError(
( ("Select “Yes” if you represent an election office. Select “No” if you dont."),
"Select “Yes” if you represent an election office. Select “No” if"
" you dont."
),
code="required", code="required",
) )
return is_election_board return is_election_board
@ -260,18 +248,13 @@ class OrganizationContactForm(RegistrarForm):
) )
city = forms.CharField( city = forms.CharField(
label="City", label="City",
error_messages={ error_messages={"required": "Enter the city where your organization is located."},
"required": "Enter the city where your organization is located."
},
) )
state_territory = forms.ChoiceField( state_territory = forms.ChoiceField(
label="State, territory, or military post", label="State, territory, or military post",
choices=[("", "--Select--")] + DomainApplication.StateTerritoryChoices.choices, choices=[("", "--Select--")] + DomainApplication.StateTerritoryChoices.choices,
error_messages={ error_messages={
"required": ( "required": ("Select the state, territory, or military post where your organization is located.")
"Select the state, territory, or military post where your organization"
" is located."
)
}, },
) )
zipcode = forms.CharField( zipcode = forms.CharField(
@ -320,9 +303,7 @@ class AboutYourOrganizationForm(RegistrarForm):
message="Response must be less than 1000 characters.", message="Response must be less than 1000 characters.",
) )
], ],
error_messages={ error_messages={"required": ("Enter more information about your organization.")},
"required": ("Enter more information about your organization.")
},
) )
@ -346,11 +327,7 @@ class AuthorizingOfficialForm(RegistrarForm):
first_name = forms.CharField( first_name = forms.CharField(
label="First name / given name", label="First name / given name",
error_messages={ error_messages={"required": ("Enter the first name / given name of your authorizing official.")},
"required": (
"Enter the first name / given name of your authorizing official."
)
},
) )
middle_name = forms.CharField( middle_name = forms.CharField(
required=False, required=False,
@ -358,11 +335,7 @@ class AuthorizingOfficialForm(RegistrarForm):
) )
last_name = forms.CharField( last_name = forms.CharField(
label="Last name / family name", label="Last name / family name",
error_messages={ error_messages={"required": ("Enter the last name / family name of your authorizing official.")},
"required": (
"Enter the last name / family name of your authorizing official."
)
},
) )
title = forms.CharField( title = forms.CharField(
label="Title or role in your organization", label="Title or role in your organization",
@ -375,17 +348,11 @@ class AuthorizingOfficialForm(RegistrarForm):
) )
email = forms.EmailField( email = forms.EmailField(
label="Email", label="Email",
error_messages={ error_messages={"invalid": ("Enter an email address in the required format, like name@example.com.")},
"invalid": (
"Enter an email address in the required format, like name@example.com."
)
},
) )
phone = PhoneNumberField( phone = PhoneNumberField(
label="Phone", label="Phone",
error_messages={ error_messages={"required": "Enter the phone number for your authorizing official."},
"required": "Enter the phone number for your authorizing official."
},
) )
@ -394,10 +361,7 @@ class CurrentSitesForm(RegistrarForm):
required=False, required=False,
label="Public website", label="Public website",
error_messages={ error_messages={
"invalid": ( "invalid": ("Enter your organization's current website in the required format, like www.city.com.")
"Enter your organization's current website in the required format, like"
" www.city.com."
)
}, },
) )
@ -410,9 +374,7 @@ class BaseCurrentSitesFormSet(RegistrarFormSet):
return website.strip() == "" return website.strip() == ""
def to_database(self, obj: DomainApplication): def to_database(self, obj: DomainApplication):
self._to_database( self._to_database(obj, self.JOIN, self.should_delete, self.pre_update, self.pre_create)
obj, self.JOIN, self.should_delete, self.pre_update, self.pre_create
)
@classmethod @classmethod
def from_database(cls, obj): def from_database(cls, obj):
@ -434,13 +396,9 @@ class AlternativeDomainForm(RegistrarForm):
requested = self.cleaned_data.get("alternative_domain", None) requested = self.cleaned_data.get("alternative_domain", None)
validated = DraftDomain.validate(requested, blank_ok=True) validated = DraftDomain.validate(requested, blank_ok=True)
except errors.ExtraDotsError: except errors.ExtraDotsError:
raise forms.ValidationError( raise forms.ValidationError(DOMAIN_API_MESSAGES["extra_dots"], code="extra_dots")
DOMAIN_API_MESSAGES["extra_dots"], code="extra_dots"
)
except errors.DomainUnavailableError: except errors.DomainUnavailableError:
raise forms.ValidationError( raise forms.ValidationError(DOMAIN_API_MESSAGES["unavailable"], code="unavailable")
DOMAIN_API_MESSAGES["unavailable"], code="unavailable"
)
except ValueError: except ValueError:
raise forms.ValidationError(DOMAIN_API_MESSAGES["invalid"], code="invalid") raise forms.ValidationError(DOMAIN_API_MESSAGES["invalid"], code="invalid")
return validated return validated
@ -471,9 +429,7 @@ class BaseAlternativeDomainFormSet(RegistrarFormSet):
return {} return {}
def to_database(self, obj: DomainApplication): def to_database(self, obj: DomainApplication):
self._to_database( self._to_database(obj, self.JOIN, self.should_delete, self.pre_update, self.pre_create)
obj, self.JOIN, self.should_delete, self.pre_update, self.pre_create
)
@classmethod @classmethod
def on_fetch(cls, query): def on_fetch(cls, query):
@ -523,17 +479,11 @@ class DotGovDomainForm(RegistrarForm):
requested = self.cleaned_data.get("requested_domain", None) requested = self.cleaned_data.get("requested_domain", None)
validated = DraftDomain.validate(requested) validated = DraftDomain.validate(requested)
except errors.BlankValueError: except errors.BlankValueError:
raise forms.ValidationError( raise forms.ValidationError(DOMAIN_API_MESSAGES["required"], code="required")
DOMAIN_API_MESSAGES["required"], code="required"
)
except errors.ExtraDotsError: except errors.ExtraDotsError:
raise forms.ValidationError( raise forms.ValidationError(DOMAIN_API_MESSAGES["extra_dots"], code="extra_dots")
DOMAIN_API_MESSAGES["extra_dots"], code="extra_dots"
)
except errors.DomainUnavailableError: except errors.DomainUnavailableError:
raise forms.ValidationError( raise forms.ValidationError(DOMAIN_API_MESSAGES["unavailable"], code="unavailable")
DOMAIN_API_MESSAGES["unavailable"], code="unavailable"
)
except ValueError: except ValueError:
raise forms.ValidationError(DOMAIN_API_MESSAGES["invalid"], code="invalid") raise forms.ValidationError(DOMAIN_API_MESSAGES["invalid"], code="invalid")
return validated return validated
@ -551,9 +501,7 @@ class PurposeForm(RegistrarForm):
message="Response must be less than 1000 characters.", message="Response must be less than 1000 characters.",
) )
], ],
error_messages={ error_messages={"required": "Describe how you'll use the .gov domain youre requesting."},
"required": "Describe how you'll use the .gov domain youre requesting."
},
) )
@ -590,20 +538,12 @@ class YourContactForm(RegistrarForm):
title = forms.CharField( title = forms.CharField(
label="Title or role in your organization", label="Title or role in your organization",
error_messages={ error_messages={
"required": ( "required": ("Enter your title or role in your organization (e.g., Chief Information Officer).")
"Enter your title or role in your organization (e.g., Chief Information"
" Officer)."
)
}, },
) )
email = forms.EmailField( email = forms.EmailField(
label="Email", label="Email",
error_messages={ error_messages={"invalid": ("Enter your email address in the required format, like name@example.com.")},
"invalid": (
"Enter your email address in the required format, like"
" name@example.com."
)
},
) )
phone = PhoneNumberField( phone = PhoneNumberField(
label="Phone", label="Phone",
@ -614,9 +554,7 @@ class YourContactForm(RegistrarForm):
class OtherContactsForm(RegistrarForm): class OtherContactsForm(RegistrarForm):
first_name = forms.CharField( first_name = forms.CharField(
label="First name / given name", label="First name / given name",
error_messages={ error_messages={"required": "Enter the first name / given name of this contact."},
"required": "Enter the first name / given name of this contact."
},
) )
middle_name = forms.CharField( middle_name = forms.CharField(
required=False, required=False,
@ -624,26 +562,19 @@ class OtherContactsForm(RegistrarForm):
) )
last_name = forms.CharField( last_name = forms.CharField(
label="Last name / family name", label="Last name / family name",
error_messages={ error_messages={"required": "Enter the last name / family name of this contact."},
"required": "Enter the last name / family name of this contact."
},
) )
title = forms.CharField( title = forms.CharField(
label="Title or role in your organization", label="Title or role in your organization",
error_messages={ error_messages={
"required": ( "required": (
"Enter the title or role in your organization of this contact (e.g.," "Enter the title or role in your organization of this contact (e.g., Chief Information Officer)."
" Chief Information Officer)."
) )
}, },
) )
email = forms.EmailField( email = forms.EmailField(
label="Email", label="Email",
error_messages={ error_messages={"invalid": ("Enter an email address in the required format, like name@example.com.")},
"invalid": (
"Enter an email address in the required format, like name@example.com."
)
},
) )
phone = PhoneNumberField( phone = PhoneNumberField(
label="Phone", label="Phone",
@ -659,9 +590,7 @@ class BaseOtherContactsFormSet(RegistrarFormSet):
return all(empty) return all(empty)
def to_database(self, obj: DomainApplication): def to_database(self, obj: DomainApplication):
self._to_database( self._to_database(obj, self.JOIN, self.should_delete, self.pre_update, self.pre_create)
obj, self.JOIN, self.should_delete, self.pre_update, self.pre_create
)
@classmethod @classmethod
def from_database(cls, obj): def from_database(cls, obj):
@ -706,9 +635,6 @@ class RequirementsForm(RegistrarForm):
is_policy_acknowledged = forms.BooleanField( is_policy_acknowledged = forms.BooleanField(
label="I read and agree to the requirements for operating .gov domains.", label="I read and agree to the requirements for operating .gov domains.",
error_messages={ error_messages={
"required": ( "required": ("Check the box if you read and agree to the requirements for operating .gov domains.")
"Check the box if you read and agree to the requirements for"
" operating .gov domains."
)
}, },
) )

View file

@ -95,23 +95,17 @@ class DomainNameserverForm(forms.Form):
elif e.code == nsErrorCodes.MISSING_IP: elif e.code == nsErrorCodes.MISSING_IP:
self.add_error( self.add_error(
"ip", "ip",
NameserverError( NameserverError(code=nsErrorCodes.MISSING_IP, nameserver=domain, ip=ip_list),
code=nsErrorCodes.MISSING_IP, nameserver=domain, ip=ip_list
),
) )
elif e.code == nsErrorCodes.MISSING_HOST: elif e.code == nsErrorCodes.MISSING_HOST:
self.add_error( self.add_error(
"server", "server",
NameserverError( NameserverError(code=nsErrorCodes.MISSING_HOST, nameserver=domain, ip=ip_list),
code=nsErrorCodes.MISSING_HOST, nameserver=domain, ip=ip_list
),
) )
elif e.code == nsErrorCodes.INVALID_HOST: elif e.code == nsErrorCodes.INVALID_HOST:
self.add_error( self.add_error(
"server", "server",
NameserverError( NameserverError(code=nsErrorCodes.INVALID_HOST, nameserver=server, ip=ip_list),
code=nsErrorCodes.INVALID_HOST, nameserver=server, ip=ip_list
),
) )
else: else:
self.add_error("ip", str(e)) self.add_error("ip", str(e))
@ -187,17 +181,12 @@ class DomainOrgNameAddressForm(forms.ModelForm):
"urbanization", "urbanization",
] ]
error_messages = { error_messages = {
"federal_agency": { "federal_agency": {"required": "Select the federal agency for your organization."},
"required": "Select the federal agency for your organization."
},
"organization_name": {"required": "Enter the name of your organization."}, "organization_name": {"required": "Enter the name of your organization."},
"address_line1": { "address_line1": {"required": "Enter the street address of your organization."},
"required": "Enter the street address of your organization."
},
"city": {"required": "Enter the city where your organization is located."}, "city": {"required": "Enter the city where your organization is located."},
"state_territory": { "state_territory": {
"required": "Select the state, territory, or military post where your" "required": "Select the state, territory, or military post where your organization is located."
"organization is located."
}, },
} }
widgets = { widgets = {
@ -205,9 +194,7 @@ class DomainOrgNameAddressForm(forms.ModelForm):
# state/territory because for these fields we are creating an individual # state/territory because for these fields we are creating an individual
# instance of the Select. For the other fields we use the for loop to set # instance of the Select. For the other fields we use the for loop to set
# the class's required attribute to true. # the class's required attribute to true.
"federal_agency": forms.Select( "federal_agency": forms.Select(attrs={"required": True}, choices=DomainInformation.AGENCY_CHOICES),
attrs={"required": True}, choices=DomainInformation.AGENCY_CHOICES
),
"organization_name": forms.TextInput, "organization_name": forms.TextInput,
"address_line1": forms.TextInput, "address_line1": forms.TextInput,
"address_line2": forms.TextInput, "address_line2": forms.TextInput,

View file

@ -23,9 +23,7 @@ class Command(BaseCommand):
default="txt", default="txt",
help="What file extensions to look for, like txt or gz", help="What file extensions to look for, like txt or gz",
) )
parser.add_argument( parser.add_argument("--directory", default="migrationdata", help="Desired directory")
"--directory", default="migrationdata", help="Desired directory"
)
def handle(self, **options): def handle(self, **options):
file_extension: str = options.get("file_extension").lstrip(".") file_extension: str = options.get("file_extension").lstrip(".")

View file

@ -52,20 +52,15 @@ class Command(BaseCommand):
if result.returncode: if result.returncode:
self.stderr.write( self.stderr.write(
self.style.NOTICE( self.style.NOTICE(
"[manage.py lint] Re-try with: [docker-compose exec app] " "[manage.py lint] Re-try with: [docker-compose exec app] " f"{' '.join(linter['args'])}"
f"{' '.join(linter['args'])}"
) )
) )
errors.append(CalledProcessError(result.returncode, linter["args"])) errors.append(CalledProcessError(result.returncode, linter["args"]))
else: else:
self.stdout.write( self.stdout.write(f"[manage.py lint] {linter['purpose']} completed with success!")
f"[manage.py lint] {linter['purpose']} completed with success!"
)
if errors: if errors:
self.stdout.write(f"[manage.py lint] {len(errors)} linter(s) failed.") self.stdout.write(f"[manage.py lint] {len(errors)} linter(s) failed.")
raise LinterError(errors) raise LinterError(errors)
except (CalledProcessError, LinterError) as e: except (CalledProcessError, LinterError) as e:
raise CommandError(e) raise CommandError(e)
self.stdout.write( self.stdout.write(self.style.SUCCESS("[manage.py lint] All linters ran successfully."))
self.style.SUCCESS("[manage.py lint] All linters ran successfully.")
)

View file

@ -21,9 +21,7 @@ class Command(BaseCommand):
"domain_contacts_filename", "domain_contacts_filename",
help="Data file with domain contact information", help="Data file with domain contact information",
) )
parser.add_argument( parser.add_argument("contacts_filename", help="Data file with contact information")
"contacts_filename", help="Data file with contact information"
)
parser.add_argument("--sep", default="|", help="Delimiter character") parser.add_argument("--sep", default="|", help="Delimiter character")

View file

@ -43,9 +43,7 @@ class Command(BaseCommand):
help = "Load domain data from a delimited text file on stdin." help = "Load domain data from a delimited text file on stdin."
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument( parser.add_argument("--sep", default="|", help="Separator character for data file")
"--sep", default="|", help="Separator character for data file"
)
def handle(self, *args, **options): def handle(self, *args, **options):
separator_character = options.get("sep") separator_character = options.get("sep")

View file

@ -1,10 +1,12 @@
import json import json
import os
import sys import sys
import csv import csv
import logging import logging
import argparse import argparse
from collections import defaultdict from collections import defaultdict
from django.conf import settings
from django.core.management import BaseCommand from django.core.management import BaseCommand
from registrar.management.commands.utility.epp_data_containers import EnumFilenames from registrar.management.commands.utility.epp_data_containers import EnumFilenames
@ -43,19 +45,14 @@ class Command(BaseCommand):
""" """
parser.add_argument( parser.add_argument(
"migration_json_filename", "migration_json_filename",
help=( help=("A JSON file that holds the location and filenames" "of all the data files used for migrations"),
"A JSON file that holds the location and filenames"
"of all the data files used for migrations"
),
) )
parser.add_argument("--sep", default="|", help="Delimiter character") parser.add_argument("--sep", default="|", help="Delimiter character")
parser.add_argument("--debug", action=argparse.BooleanOptionalAction) parser.add_argument("--debug", action=argparse.BooleanOptionalAction)
parser.add_argument( parser.add_argument("--limitParse", default=0, help="Sets max number of entries to load")
"--limitParse", default=0, help="Sets max number of entries to load"
)
parser.add_argument( parser.add_argument(
"--resetTable", "--resetTable",
@ -63,6 +60,9 @@ class Command(BaseCommand):
action=argparse.BooleanOptionalAction, action=argparse.BooleanOptionalAction,
) )
# This option should only be available when developing locally.
# This should not be available to the end user.
if settings.DEBUG:
parser.add_argument( parser.add_argument(
"--infer_filenames", "--infer_filenames",
action=argparse.BooleanOptionalAction, action=argparse.BooleanOptionalAction,
@ -70,9 +70,7 @@ class Command(BaseCommand):
"Recommended to be enabled only in a development or testing setting.", "Recommended to be enabled only in a development or testing setting.",
) )
parser.add_argument( parser.add_argument("--directory", default="migrationdata", help="Desired directory")
"--directory", default="migrationdata", help="Desired directory"
)
parser.add_argument( parser.add_argument(
"--domain_contacts_filename", "--domain_contacts_filename",
help="Data file with domain contact information", help="Data file with domain contact information",
@ -116,9 +114,7 @@ class Command(BaseCommand):
help="Defines the filename for domain type adhocs", help="Defines the filename for domain type adhocs",
) )
def print_debug_mode_statements( def print_debug_mode_statements(self, debug_on: bool, debug_max_entries_to_parse: int):
self, debug_on: bool, debug_max_entries_to_parse: int
):
"""Prints additional terminal statements to indicate if --debug """Prints additional terminal statements to indicate if --debug
or --limitParse are in use""" or --limitParse are in use"""
if debug_on: if debug_on:
@ -140,9 +136,7 @@ class Command(BaseCommand):
""" """
) )
def get_domain_user_dict( def get_domain_user_dict(self, domain_statuses_filename: str, sep: str) -> defaultdict[str, str]:
self, domain_statuses_filename: str, sep: str
) -> defaultdict[str, str]:
"""Creates a mapping of domain name -> status""" """Creates a mapping of domain name -> status"""
domain_status_dictionary = defaultdict(str) domain_status_dictionary = defaultdict(str)
logger.info("Reading domain statuses data file %s", domain_statuses_filename) logger.info("Reading domain statuses data file %s", domain_statuses_filename)
@ -154,9 +148,7 @@ class Command(BaseCommand):
logger.info("Loaded statuses for %d domains", len(domain_status_dictionary)) logger.info("Loaded statuses for %d domains", len(domain_status_dictionary))
return domain_status_dictionary return domain_status_dictionary
def get_user_emails_dict( def get_user_emails_dict(self, contacts_filename: str, sep) -> defaultdict[str, str]:
self, contacts_filename: str, sep
) -> defaultdict[str, str]:
"""Creates mapping of userId -> emails""" """Creates mapping of userId -> emails"""
user_emails_dictionary = defaultdict(str) user_emails_dictionary = defaultdict(str)
logger.info("Reading contacts data file %s", contacts_filename) logger.info("Reading contacts data file %s", contacts_filename)
@ -205,19 +197,13 @@ class Command(BaseCommand):
total_duplicate_domains = len(duplicate_domains) total_duplicate_domains = len(duplicate_domains)
total_users_without_email = len(users_without_email) total_users_without_email = len(users_without_email)
if total_users_without_email > 0: if total_users_without_email > 0:
users_without_email_as_string = "{}".format( users_without_email_as_string = "{}".format(", ".join(map(str, duplicate_domain_user_combos)))
", ".join(map(str, duplicate_domain_user_combos))
)
logger.warning( logger.warning(
f"{TerminalColors.YELLOW} No e-mails found for users: {users_without_email_as_string}" # noqa f"{TerminalColors.YELLOW} No e-mails found for users: {users_without_email_as_string}" # noqa
) )
if total_duplicate_pairs > 0 or total_duplicate_domains > 0: if total_duplicate_pairs > 0 or total_duplicate_domains > 0:
duplicate_pairs_as_string = "{}".format( duplicate_pairs_as_string = "{}".format(", ".join(map(str, duplicate_domain_user_combos)))
", ".join(map(str, duplicate_domain_user_combos)) duplicate_domains_as_string = "{}".format(", ".join(map(str, duplicate_domains)))
)
duplicate_domains_as_string = "{}".format(
", ".join(map(str, duplicate_domains))
)
logger.warning( logger.warning(
f"""{TerminalColors.YELLOW} f"""{TerminalColors.YELLOW}
@ -235,9 +221,7 @@ class Command(BaseCommand):
{TerminalColors.ENDC}""" {TerminalColors.ENDC}"""
) )
def print_summary_status_findings( def print_summary_status_findings(self, domains_without_status: list[str], outlier_statuses: list[str]):
self, domains_without_status: list[str], outlier_statuses: list[str]
):
"""Called at the end of the script execution to print out a summary of """Called at the end of the script execution to print out a summary of
status anomolies in the imported Verisign data. Currently, we check for: status anomolies in the imported Verisign data. Currently, we check for:
- domains without a status - domains without a status
@ -247,9 +231,7 @@ class Command(BaseCommand):
total_domains_without_status = len(domains_without_status) total_domains_without_status = len(domains_without_status)
total_outlier_statuses = len(outlier_statuses) total_outlier_statuses = len(outlier_statuses)
if total_domains_without_status > 0: if total_domains_without_status > 0:
domains_without_status_as_string = "{}".format( domains_without_status_as_string = "{}".format(", ".join(map(str, domains_without_status)))
", ".join(map(str, domains_without_status))
)
logger.warning( logger.warning(
f"""{TerminalColors.YELLOW} f"""{TerminalColors.YELLOW}
@ -263,9 +245,7 @@ class Command(BaseCommand):
) )
if total_outlier_statuses > 0: if total_outlier_statuses > 0:
domains_without_status_as_string = "{}".format( domains_without_status_as_string = "{}".format(", ".join(map(str, outlier_statuses))) # noqa
", ".join(map(str, outlier_statuses))
) # noqa
logger.warning( logger.warning(
f"""{TerminalColors.YELLOW} f"""{TerminalColors.YELLOW}
@ -322,6 +302,9 @@ class Command(BaseCommand):
**options, **options,
): ):
"""Parse the data files and create TransitionDomains.""" """Parse the data files and create TransitionDomains."""
if not settings.DEBUG:
options["infer_filenames"] = False
args = TransitionDomainArguments(**options) args = TransitionDomainArguments(**options)
# Desired directory for additional TransitionDomain data # Desired directory for additional TransitionDomain data
@ -366,42 +349,31 @@ class Command(BaseCommand):
debug_on = args.debug debug_on = args.debug
# Get --LimitParse argument # Get --LimitParse argument
debug_max_entries_to_parse = int( debug_max_entries_to_parse = int(args.limitParse) # set to 0 to parse all entries
args.limitParse
) # set to 0 to parse all entries
# Variables for Additional TransitionDomain Information # # Variables for Additional TransitionDomain Information #
# Main script filenames - these do not have defaults # Main script filenames - these do not have defaults
domain_contacts_filename = None domain_contacts_filename = None
try: try:
domain_contacts_filename = directory + options.get( domain_contacts_filename = directory + options.get("domain_contacts_filename")
"domain_contacts_filename"
)
except TypeError: except TypeError:
logger.error( logger.error(
f"Invalid filename of '{args.domain_contacts_filename}'" f"Invalid filename of '{args.domain_contacts_filename}'" " was provided for domain_contacts_filename"
" was provided for domain_contacts_filename"
) )
contacts_filename = None contacts_filename = None
try: try:
contacts_filename = directory + options.get("contacts_filename") contacts_filename = directory + options.get("contacts_filename")
except TypeError: except TypeError:
logger.error( logger.error(f"Invalid filename of '{args.contacts_filename}'" " was provided for contacts_filename")
f"Invalid filename of '{args.contacts_filename}'"
" was provided for contacts_filename"
)
domain_statuses_filename = None domain_statuses_filename = None
try: try:
domain_statuses_filename = directory + options.get( domain_statuses_filename = directory + options.get("domain_statuses_filename")
"domain_statuses_filename"
)
except TypeError: except TypeError:
logger.error( logger.error(
f"Invalid filename of '{args.domain_statuses_filename}'" f"Invalid filename of '{args.domain_statuses_filename}'" " was provided for domain_statuses_filename"
" was provided for domain_statuses_filename"
) )
# Agency information # Agency information
@ -419,11 +391,25 @@ class Command(BaseCommand):
# print message to terminal about which args are in use # print message to terminal about which args are in use
self.print_debug_mode_statements(debug_on, debug_max_entries_to_parse) self.print_debug_mode_statements(debug_on, debug_max_entries_to_parse)
filenames = [
agency_adhoc_filename,
domain_adhoc_filename,
organization_adhoc_filename,
domain_escrow_filename,
domain_additional_filename,
]
# Do a top-level check to see if these files exist
for filename in filenames:
if not isinstance(filename, str):
raise TypeError(f"Filename must be a string, got {type(filename).__name__}")
full_path = os.path.join(directory, filename)
if not os.path.isfile(full_path):
raise FileNotFoundError(full_path)
# STEP 1: # STEP 1:
# Create mapping of domain name -> status # Create mapping of domain name -> status
domain_status_dictionary = self.get_domain_user_dict( domain_status_dictionary = self.get_domain_user_dict(domain_statuses_filename, sep)
domain_statuses_filename, sep
)
# STEP 2: # STEP 2:
# Create mapping of userId -> email # Create mapping of userId -> email
@ -518,12 +504,7 @@ class Command(BaseCommand):
None, None,
) )
existing_domain_user_pair = next( existing_domain_user_pair = next(
( (x for x in to_create if x.username == new_entry_email and x.domain_name == new_entry_domain_name),
x
for x in to_create
if x.username == new_entry_email
and x.domain_name == new_entry_domain_name
),
None, None,
) )
if existing_domain is not None: if existing_domain is not None:
@ -594,10 +575,7 @@ class Command(BaseCommand):
) )
# Check Parse limit and exit loop if needed # Check Parse limit and exit loop if needed
if ( if total_rows_parsed >= debug_max_entries_to_parse and debug_max_entries_to_parse != 0:
total_rows_parsed >= debug_max_entries_to_parse
and debug_max_entries_to_parse != 0
):
logger.info( logger.info(
f"{TerminalColors.YELLOW}" f"{TerminalColors.YELLOW}"
f"----PARSE LIMIT REACHED. HALTING PARSER.----" f"----PARSE LIMIT REACHED. HALTING PARSER.----"
@ -606,12 +584,35 @@ class Command(BaseCommand):
break break
TransitionDomain.objects.bulk_create(to_create) TransitionDomain.objects.bulk_create(to_create)
# Print a summary of findings (duplicate entries,
# missing data..etc.)
self.print_summary_duplications(duplicate_domain_user_combos, duplicate_domains, users_without_email)
self.print_summary_status_findings(domains_without_status, outlier_statuses)
logger.info(
f"""{TerminalColors.OKGREEN}
============= FINISHED ===============
Created {total_new_entries} transition domain entries,
Updated {total_updated_domain_entries} transition domain entries
{TerminalColors.YELLOW}
----- DUPLICATES FOUND -----
{len(duplicate_domain_user_combos)} DOMAIN - USER pairs
were NOT unique in the supplied data files.
{len(duplicate_domains)} DOMAINS were NOT unique in
the supplied data files.
----- STATUSES -----
{len(domains_without_status)} DOMAINS had NO status (defaulted to READY).
{len(outlier_statuses)} Statuses were invalid (defaulted to READY).
{TerminalColors.ENDC}
"""
)
# Print a summary of findings (duplicate entries, # Print a summary of findings (duplicate entries,
# missing data..etc.) # missing data..etc.)
self.print_summary_duplications( self.print_summary_duplications(duplicate_domain_user_combos, duplicate_domains, users_without_email)
duplicate_domain_user_combos, duplicate_domains, users_without_email
)
self.print_summary_status_findings(domains_without_status, outlier_statuses) self.print_summary_status_findings(domains_without_status, outlier_statuses)
logger.info( logger.info(

View file

@ -94,33 +94,23 @@ class Command(BaseCommand):
parser.add_argument( parser.add_argument(
"--migrationJSON", "--migrationJSON",
default="migrationFilepaths.json", default="migrationFilepaths.json",
help=( help=("A JSON file that holds the location and filenames" "of all the data files used for migrations"),
"A JSON file that holds the location and filenames"
"of all the data files used for migrations"
),
) )
# TODO: deprecate this once JSON module is done? (or keep as an override) # TODO: deprecate this once JSON module is done? (or keep as an override)
parser.add_argument( parser.add_argument(
"--migrationDirectory", "--migrationDirectory",
default="migrationdata", default="migrationdata",
help=( help=("The location of the files used for load_transition_domain migration script"),
"The location of the files used for"
"load_transition_domain migration script"
),
) )
parser.add_argument( parser.add_argument("--sep", default="|", help="Delimiter character for the migration files")
"--sep", default="|", help="Delimiter character for the migration files"
)
parser.add_argument("--debug", action=argparse.BooleanOptionalAction) parser.add_argument("--debug", action=argparse.BooleanOptionalAction)
parser.add_argument("--disablePrompts", action=argparse.BooleanOptionalAction) parser.add_argument("--disablePrompts", action=argparse.BooleanOptionalAction)
parser.add_argument( parser.add_argument("--limitParse", default=0, help="Sets max number of entries to load")
"--limitParse", default=0, help="Sets max number of entries to load"
)
parser.add_argument( parser.add_argument(
"--resetTable", "--resetTable",
@ -171,9 +161,7 @@ class Command(BaseCommand):
# Check Domain table # Check Domain table
matching_domains = Domain.objects.filter(name=transition_domain_name) matching_domains = Domain.objects.filter(name=transition_domain_name)
# Check Domain Information table # Check Domain Information table
matching_domain_informations = DomainInformation.objects.filter( matching_domain_informations = DomainInformation.objects.filter(domain__name=transition_domain_name)
domain__name=transition_domain_name
)
# Check Domain Invitation table # Check Domain Invitation table
matching_domain_invitations = DomainInvitation.objects.filter( matching_domain_invitations = DomainInvitation.objects.filter(
email=transition_domain_email.lower(), email=transition_domain_email.lower(),
@ -213,15 +201,9 @@ class Command(BaseCommand):
total_missing_domain_invitations = len(missing_domain_invites) total_missing_domain_invitations = len(missing_domain_invites)
missing_domains_as_string = "{}".format(", ".join(map(str, missing_domains))) missing_domains_as_string = "{}".format(", ".join(map(str, missing_domains)))
duplicate_domains_as_string = "{}".format( duplicate_domains_as_string = "{}".format(", ".join(map(str, duplicate_domains)))
", ".join(map(str, duplicate_domains)) missing_domain_informations_as_string = "{}".format(", ".join(map(str, missing_domain_informations)))
) missing_domain_invites_as_string = "{}".format(", ".join(map(str, missing_domain_invites)))
missing_domain_informations_as_string = "{}".format(
", ".join(map(str, missing_domain_informations))
)
missing_domain_invites_as_string = "{}".format(
", ".join(map(str, missing_domain_invites))
)
logger.info( logger.info(
f"""{TerminalColors.OKGREEN} f"""{TerminalColors.OKGREEN}

View file

@ -37,10 +37,20 @@ class Command(BaseCommand):
help="Send emails ", help="Send emails ",
) )
parser.add_argument("emails", nargs="*", help="Email addresses to send invitations to")
def handle(self, **options): def handle(self, **options):
"""Process the objects in TransitionDomain.""" """Process the objects in TransitionDomain."""
logger.info("checking domains and preparing emails") logger.info("checking domains and preparing emails")
if options["emails"]:
# this option is a list of email addresses
self.transition_domains = TransitionDomain.objects.filter(
username__in=options["emails"],
email_sent=False,
).order_by("username")
else:
# Get all TransitionDomain objects # Get all TransitionDomain objects
self.transition_domains = TransitionDomain.objects.filter( self.transition_domains = TransitionDomain.objects.filter(
email_sent=False, email_sent=False,
@ -83,10 +93,7 @@ class Command(BaseCommand):
# domains_with_errors # domains_with_errors
try: try:
# if prior username does not match current username # if prior username does not match current username
if ( if not email_context["email"] or email_context["email"] != transition_domain.username:
not email_context["email"]
or email_context["email"] != transition_domain.username
):
# if not first in list of transition_domains # if not first in list of transition_domains
if email_context["email"]: if email_context["email"]:
# append the email context to the emails_to_send array # append the email context to the emails_to_send array
@ -96,12 +103,8 @@ class Command(BaseCommand):
email_context["domains"].append(transition_domain.domain_name) email_context["domains"].append(transition_domain.domain_name)
except Exception as err: except Exception as err:
# error condition if domain not in database # error condition if domain not in database
self.domains_with_errors.append( self.domains_with_errors.append(copy.deepcopy(transition_domain.domain_name))
copy.deepcopy(transition_domain.domain_name) logger.error(f"error retrieving domain {transition_domain.domain_name}: {err}")
)
logger.error(
f"error retrieving domain {transition_domain.domain_name}: {err}"
)
# if there are at least one more transition domains than errors, # if there are at least one more transition domains than errors,
# then append one more item # then append one more item
if len(self.transition_domains) > len(self.domains_with_errors): if len(self.transition_domains) > len(self.domains_with_errors):

View file

@ -43,9 +43,7 @@ class Command(BaseCommand):
# ====================================================== # ======================================================
# ===================== PRINTING ====================== # ===================== PRINTING ======================
# ====================================================== # ======================================================
def print_debug_mode_statements( def print_debug_mode_statements(self, debug_on: bool, debug_max_entries_to_parse: int):
self, debug_on: bool, debug_max_entries_to_parse: int
):
"""Prints additional terminal statements to indicate if --debug """Prints additional terminal statements to indicate if --debug
or --limitParse are in use""" or --limitParse are in use"""
TerminalHelper.print_conditional( TerminalHelper.print_conditional(
@ -67,13 +65,8 @@ class Command(BaseCommand):
""", """,
) )
def parse_limit_reached( def parse_limit_reached(self, debug_max_entries_to_parse: bool, total_rows_parsed: int) -> bool:
self, debug_max_entries_to_parse: bool, total_rows_parsed: int if debug_max_entries_to_parse > 0 and total_rows_parsed >= debug_max_entries_to_parse:
) -> bool:
if (
debug_max_entries_to_parse > 0
and total_rows_parsed >= debug_max_entries_to_parse
):
logger.info( logger.info(
f"""{TerminalColors.YELLOW} f"""{TerminalColors.YELLOW}
----PARSE LIMIT REACHED. HALTING PARSER.---- ----PARSE LIMIT REACHED. HALTING PARSER.----
@ -160,9 +153,7 @@ class Command(BaseCommand):
# ====================================================== # ======================================================
# =================== DOMAIN ===================== # =================== DOMAIN =====================
# ====================================================== # ======================================================
def update_or_create_domain( def update_or_create_domain(self, transition_domain: TransitionDomain, debug_on: bool):
self, transition_domain: TransitionDomain, debug_on: bool
):
"""Given a transition domain, either finds & updates an existing """Given a transition domain, either finds & updates an existing
corresponding domain, or creates a new corresponding domain in corresponding domain, or creates a new corresponding domain in
the Domain table. the Domain table.
@ -261,9 +252,7 @@ class Command(BaseCommand):
) )
return (target_domain, True) return (target_domain, True)
def update_domain_status( def update_domain_status(self, transition_domain: TransitionDomain, target_domain: Domain, debug_on: bool) -> bool:
self, transition_domain: TransitionDomain, target_domain: Domain, debug_on: bool
) -> bool:
"""Given a transition domain that matches an existing domain, """Given a transition domain that matches an existing domain,
updates the existing domain object with that status of updates the existing domain object with that status of
the transition domain. the transition domain.
@ -294,9 +283,7 @@ class Command(BaseCommand):
# ====================================================== # ======================================================
# ================ DOMAIN INVITATION ================== # ================ DOMAIN INVITATION ==================
# ====================================================== # ======================================================
def try_add_domain_invitation( def try_add_domain_invitation(self, domain_email: str, associated_domain: Domain) -> DomainInvitation | None:
self, domain_email: str, associated_domain: Domain
) -> DomainInvitation | None:
"""If no domain invitation exists for the given domain and """If no domain invitation exists for the given domain and
e-mail, create and return a new domain invitation object. e-mail, create and return a new domain invitation object.
If one already exists, or if the email is invalid, return NONE""" If one already exists, or if the email is invalid, return NONE"""
@ -328,26 +315,18 @@ class Command(BaseCommand):
).exists() ).exists()
if not domain_email_already_in_domain_invites: if not domain_email_already_in_domain_invites:
# Create new domain invitation # Create new domain invitation
new_domain_invitation = DomainInvitation( new_domain_invitation = DomainInvitation(email=domain_email.lower(), domain=associated_domain)
email=domain_email.lower(), domain=associated_domain
)
return new_domain_invitation return new_domain_invitation
return None return None
# ====================================================== # ======================================================
# ================ DOMAIN INFORMATION ================= # ================ DOMAIN INFORMATION =================
# ====================================================== # ======================================================
def update_domain_information( def update_domain_information(self, current: DomainInformation, target: DomainInformation, debug_on: bool) -> bool:
self, current: DomainInformation, target: DomainInformation, debug_on: bool
) -> bool:
# DEBUG: # DEBUG:
TerminalHelper.print_conditional( TerminalHelper.print_conditional(
debug_on, debug_on,
( (f"{TerminalColors.OKCYAN}" f"Updating: {current}" f"{TerminalColors.ENDC}"), # noqa
f"{TerminalColors.OKCYAN}"
f"Updating: {current}"
f"{TerminalColors.ENDC}"
), # noqa
) )
updated = False updated = False
@ -496,15 +475,11 @@ class Command(BaseCommand):
debug_on, debug_on,
) )
target_domain_information = None target_domain_information = None
domain_information_exists = DomainInformation.objects.filter( domain_information_exists = DomainInformation.objects.filter(domain__name=transition_domain_name).exists()
domain__name=transition_domain_name
).exists()
if domain_information_exists: if domain_information_exists:
try: try:
# get the existing domain information object # get the existing domain information object
target_domain_information = DomainInformation.objects.get( target_domain_information = DomainInformation.objects.get(domain__name=transition_domain_name)
domain__name=transition_domain_name
)
# DEBUG: # DEBUG:
TerminalHelper.print_conditional( TerminalHelper.print_conditional(
debug_on, debug_on,
@ -518,9 +493,7 @@ class Command(BaseCommand):
# for existing entry, update the status to # for existing entry, update the status to
# the transition domain status # the transition domain status
self.update_domain_information( self.update_domain_information(target_domain_information, template_domain_information, debug_on)
target_domain_information, template_domain_information, debug_on
)
# TODO: not all domains need to be updated # TODO: not all domains need to be updated
# (the information is the same). # (the information is the same).
# Need to bubble this up to the final report. # Need to bubble this up to the final report.
@ -590,9 +563,7 @@ class Command(BaseCommand):
if target_domain_information is None: if target_domain_information is None:
# ---------------- SKIPPED ---------------- # ---------------- SKIPPED ----------------
skipped_domain_information_entries.append(target_domain_information) skipped_domain_information_entries.append(target_domain_information)
debug_string = ( debug_string = f"skipped domain information: {target_domain_information}"
f"skipped domain information: {target_domain_information}"
)
elif was_created: elif was_created:
# DEBUG: # DEBUG:
TerminalHelper.print_conditional( TerminalHelper.print_conditional(
@ -607,11 +578,7 @@ class Command(BaseCommand):
# The unique key constraint does not allow multiple domain # The unique key constraint does not allow multiple domain
# information objects to share the same domain # information objects to share the same domain
existing_domain_information_in_to_create = next( existing_domain_information_in_to_create = next(
( (x for x in domain_information_to_create if x.domain.name == target_domain_information.domain.name),
x
for x in domain_information_to_create
if x.domain.name == target_domain_information.domain.name
),
None, None,
) )
# TODO: this is redundant. # TODO: this is redundant.
@ -620,10 +587,7 @@ class Command(BaseCommand):
existing_domain_info = DomainInformation.objects.filter( existing_domain_info = DomainInformation.objects.filter(
domain__name=target_domain_information.domain.name domain__name=target_domain_information.domain.name
).exists() ).exists()
if ( if existing_domain_information_in_to_create is not None or existing_domain_info:
existing_domain_information_in_to_create is not None
or existing_domain_info
):
debug_string = f"""{TerminalColors.YELLOW} debug_string = f"""{TerminalColors.YELLOW}
Duplicate Detected: {existing_domain_information_in_to_create}. Duplicate Detected: {existing_domain_information_in_to_create}.
Cannot add duplicate Domain Information object Cannot add duplicate Domain Information object
@ -631,15 +595,11 @@ class Command(BaseCommand):
else: else:
# ---------------- CREATED ---------------- # ---------------- CREATED ----------------
domain_information_to_create.append(target_domain_information) domain_information_to_create.append(target_domain_information)
debug_string = ( debug_string = f"created domain information: {target_domain_information}"
f"created domain information: {target_domain_information}"
)
elif not was_created: elif not was_created:
# ---------------- UPDATED ---------------- # ---------------- UPDATED ----------------
updated_domain_information.append(target_domain_information) updated_domain_information.append(target_domain_information)
debug_string = ( debug_string = f"updated domain information: {target_domain_information}"
f"updated domain information: {target_domain_information}"
)
else: else:
debug_string = "domain information already exists and " debug_string = "domain information already exists and "
f"matches incoming data (NO CHANGES MADE): {target_domain_information}" f"matches incoming data (NO CHANGES MADE): {target_domain_information}"
@ -694,9 +654,7 @@ class Command(BaseCommand):
# ====================================================== # ======================================================
# ====================== DOMAIN ======================= # ====================== DOMAIN =======================
target_domain, was_created = self.update_or_create_domain( target_domain, was_created = self.update_or_create_domain(transition_domain, debug_on)
transition_domain, debug_on
)
debug_string = "" debug_string = ""
if target_domain is None: if target_domain is None:
@ -734,9 +692,7 @@ class Command(BaseCommand):
# ====================================================== # ======================================================
# ================ DOMAIN INVITATIONS ================== # ================ DOMAIN INVITATIONS ==================
new_domain_invitation = self.try_add_domain_invitation( new_domain_invitation = self.try_add_domain_invitation(transition_domain_email, target_domain)
transition_domain_email, target_domain
)
if new_domain_invitation is None: if new_domain_invitation is None:
logger.info( logger.info(
f"{TerminalColors.YELLOW} ! No new e-mail detected !" # noqa f"{TerminalColors.YELLOW} ! No new e-mail detected !" # noqa
@ -774,9 +730,7 @@ class Command(BaseCommand):
# grab command line arguments and store locally... # grab command line arguments and store locally...
debug_on = options.get("debug") debug_on = options.get("debug")
debug_max_entries_to_parse = int( debug_max_entries_to_parse = int(options.get("limitParse")) # set to 0 to parse all entries
options.get("limitParse")
) # set to 0 to parse all entries
self.print_debug_mode_statements(debug_on, debug_max_entries_to_parse) self.print_debug_mode_statements(debug_on, debug_max_entries_to_parse)
@ -844,19 +798,11 @@ class Command(BaseCommand):
invitation.domain = existing_domain.get() invitation.domain = existing_domain.get()
else: else:
# Raise an err for now # Raise an err for now
raise Exception( raise Exception(f"Domain {existing_domain} wants to be added" "but doesn't exist in the DB")
f"Domain {existing_domain} wants to be added"
"but doesn't exist in the DB"
)
invitation.save() invitation.save()
valid_org_choices = [ valid_org_choices = [(name, value) for name, value in DomainApplication.OrganizationChoices.choices]
(name, value) valid_fed_choices = [value for name, value in DomainApplication.BranchChoices.choices]
for name, value in DomainApplication.OrganizationChoices.choices
]
valid_fed_choices = [
value for name, value in DomainApplication.BranchChoices.choices
]
valid_agency_choices = DomainApplication.AGENCIES valid_agency_choices = DomainApplication.AGENCIES
# ====================================================== # ======================================================
# ================= DOMAIN INFORMATION ================= # ================= DOMAIN INFORMATION =================
@ -884,11 +830,7 @@ class Command(BaseCommand):
TerminalHelper.print_conditional( TerminalHelper.print_conditional(
debug_on, debug_on,
( (f"{TerminalColors.YELLOW}" f"Trying to add: {domain_information_to_create}" f"{TerminalColors.ENDC}"),
f"{TerminalColors.YELLOW}"
f"Trying to add: {domain_information_to_create}"
f"{TerminalColors.ENDC}"
),
) )
DomainInformation.objects.bulk_create(domain_information_to_create) DomainInformation.objects.bulk_create(domain_information_to_create)

View file

@ -111,9 +111,7 @@ class FileTransitionLog:
"""Logs every LogItem contained in this object""" """Logs every LogItem contained in this object"""
for parent_log in self.logs: for parent_log in self.logs:
for child_log in parent_log: for child_log in parent_log:
TerminalHelper.print_conditional( TerminalHelper.print_conditional(True, child_log.message, child_log.severity)
True, child_log.message, child_log.severity
)
def display_logs_by_domain_name(self, domain_name, restrict_type=LogCode.DEFAULT): def display_logs_by_domain_name(self, domain_name, restrict_type=LogCode.DEFAULT):
"""Displays all logs of a given domain_name. """Displays all logs of a given domain_name.
@ -130,9 +128,7 @@ class FileTransitionLog:
return None return None
for log in domain_logs: for log in domain_logs:
TerminalHelper.print_conditional( TerminalHelper.print_conditional(restrict_type != log.code, log.message, log.code)
restrict_type != log.code, log.message, log.code
)
def get_logs(self, file_type, domain_name): def get_logs(self, file_type, domain_name):
"""Grabs the logs associated with """Grabs the logs associated with
@ -166,38 +162,21 @@ class LoadExtraTransitionDomain:
updated_transition_domain = transition_domain updated_transition_domain = transition_domain
try: try:
# STEP 1: Parse organization data # STEP 1: Parse organization data
updated_transition_domain = self.parse_org_data( updated_transition_domain = self.parse_org_data(domain_name, transition_domain)
domain_name, transition_domain
)
# STEP 2: Parse domain type data # STEP 2: Parse domain type data
updated_transition_domain = self.parse_domain_type_data( updated_transition_domain = self.parse_domain_type_data(domain_name, transition_domain)
domain_name, transition_domain
)
# STEP 3: Parse authority data # STEP 3: Parse agency data
updated_transition_domain = self.parse_authority_data( updated_transition_domain = self.parse_agency_data(domain_name, transition_domain)
domain_name, transition_domain
)
# STEP 4: Parse agency data # STEP 4: Parse creation and expiration data
updated_transition_domain = self.parse_agency_data( updated_transition_domain = self.parse_creation_expiration_data(domain_name, transition_domain)
domain_name, transition_domain
)
# STEP 5: Parse creation and expiration data
updated_transition_domain = self.parse_creation_expiration_data(
domain_name, transition_domain
)
# Check if the instance has changed before saving # Check if the instance has changed before saving
updated_transition_domain.save() updated_transition_domain.save()
updated_transition_domains.append(updated_transition_domain) updated_transition_domains.append(updated_transition_domain)
logger.info( logger.info(f"{TerminalColors.OKCYAN}" f"Successfully updated {domain_name}" f"{TerminalColors.ENDC}")
f"{TerminalColors.OKCYAN}"
f"Successfully updated {domain_name}"
f"{TerminalColors.ENDC}"
)
# If we run into an exception on this domain, # If we run into an exception on this domain,
# Just skip over it and log that it happened. # Just skip over it and log that it happened.
@ -272,8 +251,7 @@ class LoadExtraTransitionDomain:
self.parse_logs.create_log_item( self.parse_logs.create_log_item(
EnumFilenames.DOMAIN_ESCROW, EnumFilenames.DOMAIN_ESCROW,
LogCode.ERROR, LogCode.ERROR,
"Could not add epp_creation_date and epp_expiration_date " "Could not add epp_creation_date and epp_expiration_date " f"on {domain_name}, no data exists.",
f"on {domain_name}, no data exists.",
domain_name, domain_name,
not self.debug, not self.debug,
) )
@ -375,10 +353,7 @@ class LoadExtraTransitionDomain:
) )
return transition_domain return transition_domain
agency_exists = ( agency_exists = transition_domain.federal_agency is not None and transition_domain.federal_agency.strip() != ""
transition_domain.federal_agency is not None
and transition_domain.federal_agency.strip() != ""
)
if not isinstance(info.active, str) or not info.active.lower() == "y": if not isinstance(info.active, str) or not info.active.lower() == "y":
self.parse_logs.create_log_item( self.parse_logs.create_log_item(
@ -393,12 +368,11 @@ class LoadExtraTransitionDomain:
if not isinstance(info.isfederal, str) or not info.isfederal.lower() == "y": if not isinstance(info.isfederal, str) or not info.isfederal.lower() == "y":
self.parse_logs.create_log_item( self.parse_logs.create_log_item(
EnumFilenames.DOMAIN_ADHOC, EnumFilenames.DOMAIN_ADHOC,
LogCode.ERROR, LogCode.INFO,
f"Could not add non-federal agency {info.agencyname} on {domain_name}", f"Adding non-federal agency {info.agencyname} on {domain_name}",
domain_name, domain_name,
not self.debug, not self.debug,
) )
return transition_domain
transition_domain.federal_agency = info.agencyname transition_domain.federal_agency = info.agencyname
@ -414,9 +388,7 @@ class LoadExtraTransitionDomain:
return transition_domain return transition_domain
def parse_domain_type_data( def parse_domain_type_data(self, domain_name, transition_domain: TransitionDomain) -> TransitionDomain:
self, domain_name, transition_domain: TransitionDomain
) -> TransitionDomain:
"""Grabs organization_type and federal_type from the parsed files """Grabs organization_type and federal_type from the parsed files
and associates it with a transition_domain object, then returns that object.""" and associates it with a transition_domain object, then returns that object."""
if not isinstance(transition_domain, TransitionDomain): if not isinstance(transition_domain, TransitionDomain):
@ -461,12 +433,10 @@ class LoadExtraTransitionDomain:
# Are we updating data that already exists, # Are we updating data that already exists,
# or are we adding new data in its place? # or are we adding new data in its place?
organization_type_exists = ( organization_type_exists = (
transition_domain.organization_type is not None transition_domain.organization_type is not None and transition_domain.organization_type.strip() != ""
and transition_domain.organization_type.strip() != ""
) )
federal_type_exists = ( federal_type_exists = (
transition_domain.federal_type is not None transition_domain.federal_type is not None and transition_domain.federal_type.strip() != ""
and transition_domain.federal_type.strip() != ""
) )
# If we get two records, then we know it is federal. # If we get two records, then we know it is federal.
@ -500,9 +470,7 @@ class LoadExtraTransitionDomain:
return transition_domain return transition_domain
def parse_org_data( def parse_org_data(self, domain_name, transition_domain: TransitionDomain) -> TransitionDomain:
self, domain_name, transition_domain: TransitionDomain
) -> TransitionDomain:
"""Grabs organization_name from the parsed files and associates it """Grabs organization_name from the parsed files and associates it
with a transition_domain object, then returns that object.""" with a transition_domain object, then returns that object."""
if not isinstance(transition_domain, TransitionDomain): if not isinstance(transition_domain, TransitionDomain):
@ -520,8 +488,7 @@ class LoadExtraTransitionDomain:
return transition_domain return transition_domain
desired_property_exists = ( desired_property_exists = (
transition_domain.organization_name is not None transition_domain.organization_name is not None and transition_domain.organization_name.strip() != ""
and transition_domain.organization_name.strip() != ""
) )
transition_domain.organization_name = org_info.orgname transition_domain.organization_name = org_info.orgname
@ -538,9 +505,7 @@ class LoadExtraTransitionDomain:
return transition_domain return transition_domain
def _add_or_change_message( def _add_or_change_message(self, file_type, var_name, changed_value, domain_name, is_update=False):
self, file_type, var_name, changed_value, domain_name, is_update=False
):
"""Creates a log instance when a property """Creates a log instance when a property
is successfully changed on a given TransitionDomain.""" is successfully changed on a given TransitionDomain."""
if not is_update: if not is_update:
@ -744,6 +709,10 @@ class FileDataHolder:
# Object data # # Object data #
self.data: Dict[str, type] = {} self.data: Dict[str, type] = {}
# This is used ONLY for development purposes. This behaviour
# is controlled by the --infer_filename flag which is defaulted
# to false. The purpose of this check is to speed up development,
# but it cannot be used by the enduser
def try_infer_filename(self, current_file_name, default_file_name): def try_infer_filename(self, current_file_name, default_file_name):
"""Tries to match a given filename to a regex, """Tries to match a given filename to a regex,
then uses that match to generate the filename.""" then uses that match to generate the filename."""
@ -909,7 +878,6 @@ class ExtraTransitionDomain:
infer_filenames: bool -> Determines if we should try to infer_filenames: bool -> Determines if we should try to
infer the filename if a default is passed in infer the filename if a default is passed in
""" """
self.clear_file_data()
for name, value in self.file_data.items(): for name, value in self.file_data.items():
is_domain_escrow = name == EnumFilenames.DOMAIN_ESCROW is_domain_escrow = name == EnumFilenames.DOMAIN_ESCROW
filename = f"{value.filename}" filename = f"{value.filename}"
@ -924,8 +892,9 @@ class ExtraTransitionDomain:
) )
else: else:
if not infer_filenames: if not infer_filenames:
logger.error(f"Could not find file: {filename}") raise FileNotFoundError(
continue f"{TerminalColors.FAIL}" f"Could not find file {filename} for {name}" f"{TerminalColors.ENDC}"
)
# Infer filename logic # # Infer filename logic #
# This mode is used for # This mode is used for
@ -956,25 +925,22 @@ class ExtraTransitionDomain:
is_domain_escrow, is_domain_escrow,
) )
continue continue
# Log if we can't find the desired file raise FileNotFoundError(
logger.error(f"Could not find file: {filename}") f"{TerminalColors.FAIL}" f"Could not find file {filename} for {name}" f"{TerminalColors.ENDC}"
)
def clear_file_data(self): def clear_file_data(self):
for item in self.file_data.values(): for item in self.file_data.values():
file_type: FileDataHolder = item file_type: FileDataHolder = item
file_type.data = {} file_type.data = {}
def parse_csv_file( def parse_csv_file(self, file, seperator, dataclass_type, id_field, is_domain_escrow=False):
self, file, seperator, dataclass_type, id_field, is_domain_escrow=False
):
# Domain escrow is an edge case # Domain escrow is an edge case
if is_domain_escrow: if is_domain_escrow:
item_to_return = self._read_domain_escrow(file, seperator) item_to_return = self._read_domain_escrow(file, seperator)
return item_to_return return item_to_return
else: else:
item_to_return = self._read_csv_file( item_to_return = self._read_csv_file(file, seperator, dataclass_type, id_field)
file, seperator, dataclass_type, id_field
)
return item_to_return return item_to_return
# Domain escrow is an edgecase given that its structured differently data-wise. # Domain escrow is an edgecase given that its structured differently data-wise.
@ -989,9 +955,7 @@ class ExtraTransitionDomain:
creation_date = datetime.strptime(row[7], date_format) creation_date = datetime.strptime(row[7], date_format)
expiration_date = datetime.strptime(row[11], date_format) expiration_date = datetime.strptime(row[11], date_format)
dict_data[domain_name] = DomainEscrow( dict_data[domain_name] = DomainEscrow(domain_name, creation_date, expiration_date)
domain_name, creation_date, expiration_date
)
return dict_data return dict_data
def _grab_row_id(self, row, id_field, file, dataclass_type): def _grab_row_id(self, row, id_field, file, dataclass_type):
@ -1024,9 +988,7 @@ class ExtraTransitionDomain:
f"Found bad data in {file}. Attempting to clean." f"Found bad data in {file}. Attempting to clean."
f"{TerminalColors.ENDC}" f"{TerminalColors.ENDC}"
) )
updated_file_content = self.replace_bad_seperators( updated_file_content = self.replace_bad_seperators(file, f"{seperator}", ";badseperator;")
file, f"{seperator}", ";badseperator;"
)
dict_data = {} dict_data = {}
break break
@ -1040,11 +1002,7 @@ class ExtraTransitionDomain:
# After we clean the data, try to parse it again # After we clean the data, try to parse it again
if updated_file_content: if updated_file_content:
logger.info( logger.info(f"{TerminalColors.MAGENTA}" f"Retrying load for {file}" f"{TerminalColors.ENDC}")
f"{TerminalColors.MAGENTA}"
f"Retrying load for {file}"
f"{TerminalColors.ENDC}"
)
# Store the file locally rather than writing to the file. # Store the file locally rather than writing to the file.
# This is to avoid potential data corruption. # This is to avoid potential data corruption.
updated_file = io.StringIO(updated_file_content) updated_file = io.StringIO(updated_file_content)
@ -1055,9 +1013,7 @@ class ExtraTransitionDomain:
# is wrong with the file. # is wrong with the file.
if None in row: if None in row:
logger.error( logger.error(
f"{TerminalColors.FAIL}" f"{TerminalColors.FAIL}" f"Corrupt data found for {row_id}. Skipping." f"{TerminalColors.ENDC}"
f"Corrupt data found for {row_id}. Skipping."
f"{TerminalColors.ENDC}"
) )
continue continue

View file

@ -150,9 +150,7 @@ class TerminalHelper:
logger.info(print_statement) logger.info(print_statement)
@staticmethod @staticmethod
def prompt_for_execution( def prompt_for_execution(system_exit_on_terminate: bool, info_to_inspect: str, prompt_title: str) -> bool:
system_exit_on_terminate: bool, info_to_inspect: str, prompt_title: str
) -> bool:
"""Create to reduce code complexity. """Create to reduce code complexity.
Prompts the user to inspect the given string Prompts the user to inspect the given string
and asks if they wish to proceed. and asks if they wish to proceed.
@ -195,9 +193,7 @@ class TerminalHelper:
return total_line return total_line
@staticmethod @staticmethod
def print_to_file_conditional( def print_to_file_conditional(print_condition: bool, filename: str, file_directory: str, file_contents: str):
print_condition: bool, filename: str, file_directory: str, file_contents: str
):
"""Sometimes logger outputs get insanely huge.""" """Sometimes logger outputs get insanely huge."""
if print_condition: if print_condition:
# Add a slash if the last character isn't one # Add a slash if the last character isn't one
@ -206,54 +202,6 @@ class TerminalHelper:
# Assemble filepath # Assemble filepath
filepath = f"{file_directory}{filename}.txt" filepath = f"{file_directory}{filename}.txt"
# Write to file # Write to file
logger.info( logger.info(f"{TerminalColors.MAGENTA}Writing to file " f" {filepath}..." f"{TerminalColors.ENDC}")
f"{TerminalColors.MAGENTA}Writing to file "
f" {filepath}..."
f"{TerminalColors.ENDC}"
)
with open(f"{filepath}", "w+") as f: with open(f"{filepath}", "w+") as f:
f.write(file_contents) f.write(file_contents)
@staticmethod
def printProgressBar(
iteration,
total,
prefix="Progress:",
suffix="Complete",
decimals=1,
length=100,
fill="",
printEnd="\r",
):
"""
Call in a loop to create terminal progress bar
@params:
iteration - Required : current iteration (Int)
total - Required : total iterations (Int)
prefix - Optional : prefix string (Str)
suffix - Optional : suffix string (Str)
decimals - Optional : positive number of decimals in percent complete (Int)
length - Optional : character length of bar (Int)
fill - Optional : bar fill character (Str)
printEnd - Optional : end character (e.g. "\r", "\r\n") (Str)
""" # noqa
"""
# Initial call to print 0% progress
printProgressBar(0, l, prefix = 'Progress:', suffix = 'Complete', length = 50)
for i, item in enumerate(items):
# Do stuff...
time.sleep(0.1)
# Update Progress Bar
printProgressBar(i + 1, l, prefix = 'Progress:', suffix = 'Complete', length = 50)
""" # noqa
percent = ("{0:." + str(decimals) + "f}").format(
100 * (iteration / float(total))
)
filledLength = int(length * iteration // total)
bar = fill * filledLength + "-" * (length - filledLength)
print(f"\r{prefix} |{bar}| {percent}% {suffix}", end=printEnd)
# Print New Line on Complete
if iteration == total:
print()

View file

@ -37,26 +37,14 @@ class TransitionDomainArguments:
# Filenames # # Filenames #
# = Adhocs =# # = Adhocs =#
agency_adhoc_filename: Optional[str] = field( agency_adhoc_filename: Optional[str] = field(default=EnumFilenames.AGENCY_ADHOC.value[1], repr=True)
default=EnumFilenames.AGENCY_ADHOC.value[1], repr=True domain_adhoc_filename: Optional[str] = field(default=EnumFilenames.DOMAIN_ADHOC.value[1], repr=True)
) organization_adhoc_filename: Optional[str] = field(default=EnumFilenames.ORGANIZATION_ADHOC.value[1], repr=True)
domain_adhoc_filename: Optional[str] = field( authority_adhoc_filename: Optional[str] = field(default=EnumFilenames.AUTHORITY_ADHOC.value[1], repr=True)
default=EnumFilenames.DOMAIN_ADHOC.value[1], repr=True
)
organization_adhoc_filename: Optional[str] = field(
default=EnumFilenames.ORGANIZATION_ADHOC.value[1], repr=True
)
authority_adhoc_filename: Optional[str] = field(
default=EnumFilenames.AUTHORITY_ADHOC.value[1], repr=True
)
# = Data files =# # = Data files =#
domain_escrow_filename: Optional[str] = field( domain_escrow_filename: Optional[str] = field(default=EnumFilenames.DOMAIN_ESCROW.value[1], repr=True)
default=EnumFilenames.DOMAIN_ESCROW.value[1], repr=True domain_additional_filename: Optional[str] = field(default=EnumFilenames.DOMAIN_ADDITIONAL.value[1], repr=True)
)
domain_additional_filename: Optional[str] = field(
default=EnumFilenames.DOMAIN_ADDITIONAL.value[1], repr=True
)
domain_contacts_filename: Optional[str] = field(default=None, repr=True) domain_contacts_filename: Optional[str] = field(default=None, repr=True)
domain_statuses_filename: Optional[str] = field(default=None, repr=True) domain_statuses_filename: Optional[str] = field(default=None, repr=True)
contacts_filename: Optional[str] = field(default=None, repr=True) contacts_filename: Optional[str] = field(default=None, repr=True)

View file

@ -32,9 +32,7 @@ class Migration(migrations.Migration):
("password", models.CharField(max_length=128, verbose_name="password")), ("password", models.CharField(max_length=128, verbose_name="password")),
( (
"last_login", "last_login",
models.DateTimeField( models.DateTimeField(blank=True, null=True, verbose_name="last login"),
blank=True, null=True, verbose_name="last login"
),
), ),
( (
"is_superuser", "is_superuser",
@ -47,35 +45,25 @@ class Migration(migrations.Migration):
( (
"username", "username",
models.CharField( models.CharField(
error_messages={ error_messages={"unique": "A user with that username already exists."},
"unique": "A user with that username already exists."
},
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
max_length=150, max_length=150,
unique=True, unique=True,
validators=[ validators=[django.contrib.auth.validators.UnicodeUsernameValidator()],
django.contrib.auth.validators.UnicodeUsernameValidator()
],
verbose_name="username", verbose_name="username",
), ),
), ),
( (
"first_name", "first_name",
models.CharField( models.CharField(blank=True, max_length=150, verbose_name="first name"),
blank=True, max_length=150, verbose_name="first name"
),
), ),
( (
"last_name", "last_name",
models.CharField( models.CharField(blank=True, max_length=150, verbose_name="last name"),
blank=True, max_length=150, verbose_name="last name"
),
), ),
( (
"email", "email",
models.EmailField( models.EmailField(blank=True, max_length=254, verbose_name="email address"),
blank=True, max_length=254, verbose_name="email address"
),
), ),
( (
"is_staff", "is_staff",
@ -95,9 +83,7 @@ class Migration(migrations.Migration):
), ),
( (
"date_joined", "date_joined",
models.DateTimeField( models.DateTimeField(default=django.utils.timezone.now, verbose_name="date joined"),
default=django.utils.timezone.now, verbose_name="date joined"
),
), ),
( (
"groups", "groups",
@ -145,9 +131,7 @@ class Migration(migrations.Migration):
), ),
( (
"first_name", "first_name",
models.TextField( models.TextField(blank=True, db_index=True, help_text="First name", null=True),
blank=True, db_index=True, help_text="First name", null=True
),
), ),
( (
"middle_name", "middle_name",
@ -155,22 +139,16 @@ class Migration(migrations.Migration):
), ),
( (
"last_name", "last_name",
models.TextField( models.TextField(blank=True, db_index=True, help_text="Last name", null=True),
blank=True, db_index=True, help_text="Last name", null=True
),
), ),
("title", models.TextField(blank=True, help_text="Title", null=True)), ("title", models.TextField(blank=True, help_text="Title", null=True)),
( (
"email", "email",
models.TextField( models.TextField(blank=True, db_index=True, help_text="Email", null=True),
blank=True, db_index=True, help_text="Email", null=True
),
), ),
( (
"phone", "phone",
models.TextField( models.TextField(blank=True, db_index=True, help_text="Phone", null=True),
blank=True, db_index=True, help_text="Phone", null=True
),
), ),
], ],
), ),
@ -280,21 +258,15 @@ class Migration(migrations.Migration):
), ),
( (
"unit_type", "unit_type",
models.CharField( models.CharField(blank=True, help_text="Unit type", max_length=15, null=True),
blank=True, help_text="Unit type", max_length=15, null=True
),
), ),
( (
"unit_number", "unit_number",
models.CharField( models.CharField(blank=True, help_text="Unit number", max_length=255, null=True),
blank=True, help_text="Unit number", max_length=255, null=True
),
), ),
( (
"state_territory", "state_territory",
models.CharField( models.CharField(blank=True, help_text="State/Territory", max_length=2, null=True),
blank=True, help_text="State/Territory", max_length=2, null=True
),
), ),
( (
"zip_code", "zip_code",
@ -308,9 +280,7 @@ class Migration(migrations.Migration):
), ),
( (
"purpose", "purpose",
models.TextField( models.TextField(blank=True, help_text="Purpose of the domain", null=True),
blank=True, help_text="Purpose of the domain", null=True
),
), ),
( (
"security_email", "security_email",
@ -323,9 +293,7 @@ class Migration(migrations.Migration):
), ),
( (
"anything_else", "anything_else",
models.TextField( models.TextField(blank=True, help_text="Anything else we should know?", null=True),
blank=True, help_text="Anything else we should know?", null=True
),
), ),
( (
"acknowledged_policy", "acknowledged_policy",
@ -337,9 +305,7 @@ class Migration(migrations.Migration):
), ),
( (
"alternative_domains", "alternative_domains",
models.ManyToManyField( models.ManyToManyField(blank=True, related_name="alternatives+", to="registrar.website"),
blank=True, related_name="alternatives+", to="registrar.website"
),
), ),
( (
"authorizing_official", "authorizing_official",
@ -361,9 +327,7 @@ class Migration(migrations.Migration):
), ),
( (
"current_websites", "current_websites",
models.ManyToManyField( models.ManyToManyField(blank=True, related_name="current+", to="registrar.website"),
blank=True, related_name="current+", to="registrar.website"
),
), ),
( (
"investigator", "investigator",

View file

@ -48,9 +48,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="domainapplication", model_name="domainapplication",
name="address_line2", name="address_line2",
field=models.CharField( field=models.CharField(blank=True, help_text="Address line 2", max_length=15, null=True),
blank=True, help_text="Address line 2", max_length=15, null=True
),
), ),
migrations.AddField( migrations.AddField(
model_name="domainapplication", model_name="domainapplication",

View file

@ -22,8 +22,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="domainapplication", model_name="domainapplication",
name="federal_agency", name="federal_agency",
field=models.TextField( field=models.TextField(blank=True, help_text="Top level federal agency", null=True),
blank=True, help_text="Top level federal agency", null=True
),
), ),
] ]

View file

@ -21,9 +21,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="domainapplication", model_name="domainapplication",
name="type_of_work", name="type_of_work",
field=models.TextField( field=models.TextField(blank=True, help_text="Type of work of the organization", null=True),
blank=True, help_text="Type of work of the organization", null=True
),
), ),
migrations.AlterField( migrations.AlterField(
model_name="domainapplication", model_name="domainapplication",
@ -33,9 +31,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="domainapplication", model_name="domainapplication",
name="address_line2", name="address_line2",
field=models.CharField( field=models.CharField(blank=True, help_text="Street address line 2", max_length=15, null=True),
blank=True, help_text="Street address line 2", max_length=15, null=True
),
), ),
migrations.AlterField( migrations.AlterField(
model_name="domainapplication", model_name="domainapplication",
@ -95,9 +91,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="domainapplication", model_name="domainapplication",
name="purpose", name="purpose",
field=models.TextField( field=models.TextField(blank=True, help_text="Purpose of your domain", null=True),
blank=True, help_text="Purpose of your domain", null=True
),
), ),
migrations.AlterField( migrations.AlterField(
model_name="domainapplication", model_name="domainapplication",
@ -112,9 +106,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="domainapplication", model_name="domainapplication",
name="urbanization", name="urbanization",
field=models.TextField( field=models.TextField(blank=True, help_text="Urbanization (Puerto Rico only)", null=True),
blank=True, help_text="Urbanization (Puerto Rico only)", null=True
),
), ),
migrations.AlterField( migrations.AlterField(
model_name="domainapplication", model_name="domainapplication",

View file

@ -21,9 +21,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="contact", model_name="contact",
name="created_at", name="created_at",
field=models.DateTimeField( field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
auto_now_add=True, default=django.utils.timezone.now
),
preserve_default=False, preserve_default=False,
), ),
migrations.AddField( migrations.AddField(
@ -34,9 +32,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="website", model_name="website",
name="created_at", name="created_at",
field=models.DateTimeField( field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
auto_now_add=True, default=django.utils.timezone.now
),
preserve_default=False, preserve_default=False,
), ),
migrations.AddField( migrations.AddField(

View file

@ -12,16 +12,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="domainapplication", model_name="domainapplication",
name="federally_recognized_tribe", name="federally_recognized_tribe",
field=models.BooleanField( field=models.BooleanField(help_text="Is the tribe federally recognized", null=True),
help_text="Is the tribe federally recognized", null=True
),
), ),
migrations.AddField( migrations.AddField(
model_name="domainapplication", model_name="domainapplication",
name="state_recognized_tribe", name="state_recognized_tribe",
field=models.BooleanField( field=models.BooleanField(help_text="Is the tribe recognized by a state", null=True),
help_text="Is the tribe recognized by a state", null=True
),
), ),
migrations.AddField( migrations.AddField(
model_name="domainapplication", model_name="domainapplication",

View file

@ -59,8 +59,6 @@ class Migration(migrations.Migration):
), ),
migrations.AddConstraint( migrations.AddConstraint(
model_name="userdomainrole", model_name="userdomainrole",
constraint=models.UniqueConstraint( constraint=models.UniqueConstraint(fields=("user", "domain"), name="unique_user_domain_role"),
fields=("user", "domain"), name="unique_user_domain_role"
),
), ),
] ]

View file

@ -64,15 +64,11 @@ class Migration(migrations.Migration):
), ),
( (
"federally_recognized_tribe", "federally_recognized_tribe",
models.BooleanField( models.BooleanField(help_text="Is the tribe federally recognized", null=True),
help_text="Is the tribe federally recognized", null=True
),
), ),
( (
"state_recognized_tribe", "state_recognized_tribe",
models.BooleanField( models.BooleanField(help_text="Is the tribe recognized by a state", null=True),
help_text="Is the tribe recognized by a state", null=True
),
), ),
( (
"tribe_name", "tribe_name",
@ -172,9 +168,7 @@ class Migration(migrations.Migration):
), ),
( (
"purpose", "purpose",
models.TextField( models.TextField(blank=True, help_text="Purpose of your domain", null=True),
blank=True, help_text="Purpose of your domain", null=True
),
), ),
( (
"no_other_contacts_rationale", "no_other_contacts_rationale",
@ -186,9 +180,7 @@ class Migration(migrations.Migration):
), ),
( (
"anything_else", "anything_else",
models.TextField( models.TextField(blank=True, help_text="Anything else we should know?", null=True),
blank=True, help_text="Anything else we should know?", null=True
),
), ),
( (
"is_policy_acknowledged", "is_policy_acknowledged",

View file

@ -76,9 +76,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="publiccontact", model_name="publiccontact",
name="org", name="org",
field=models.TextField( field=models.TextField(help_text="Contact's organization (null ok)", null=True),
help_text="Contact's organization (null ok)", null=True
),
), ),
migrations.AlterField( migrations.AlterField(
model_name="publiccontact", model_name="publiccontact",
@ -88,9 +86,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="publiccontact", model_name="publiccontact",
name="pw", name="pw",
field=models.TextField( field=models.TextField(help_text="Contact's authorization code. 16 characters minimum."),
help_text="Contact's authorization code. 16 characters minimum."
),
), ),
migrations.AlterField( migrations.AlterField(
model_name="publiccontact", model_name="publiccontact",
@ -115,8 +111,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="publiccontact", model_name="publiccontact",
name="voice", name="voice",
field=models.TextField( field=models.TextField(help_text="Contact's phone number. Must be in ITU.E164.2005 format"),
help_text="Contact's phone number. Must be in ITU.E164.2005 format"
),
), ),
] ]

View file

@ -12,8 +12,6 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="contact", model_name="contact",
name="email", name="email",
field=models.EmailField( field=models.EmailField(blank=True, db_index=True, help_text="Email", max_length=254, null=True),
blank=True, db_index=True, help_text="Email", max_length=254, null=True
),
), ),
] ]

View file

@ -12,15 +12,11 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name="domainapplication", model_name="domainapplication",
name="address_line2", name="address_line2",
field=models.TextField( field=models.TextField(blank=True, help_text="Street address line 2", null=True),
blank=True, help_text="Street address line 2", null=True
),
), ),
migrations.AlterField( migrations.AlterField(
model_name="domaininformation", model_name="domaininformation",
name="address_line2", name="address_line2",
field=models.TextField( field=models.TextField(blank=True, help_text="Street address line 2", null=True),
blank=True, help_text="Street address line 2", null=True
),
), ),
] ]

View file

@ -95,16 +95,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="domainapplication", model_name="domainapplication",
name="about_your_organization", name="about_your_organization",
field=models.TextField( field=models.TextField(blank=True, help_text="Information about your organization", null=True),
blank=True, help_text="Information about your organization", null=True
),
), ),
migrations.AddField( migrations.AddField(
model_name="domaininformation", model_name="domaininformation",
name="about_your_organization", name="about_your_organization",
field=models.TextField( field=models.TextField(blank=True, help_text="Information about your organization", null=True),
blank=True, help_text="Information about your organization", null=True
),
), ),
migrations.AlterField( migrations.AlterField(
model_name="domainapplication", model_name="domainapplication",

View file

@ -13,7 +13,7 @@ class Migration(migrations.Migration):
model_name="domain", model_name="domain",
name="expiration_date", name="expiration_date",
field=models.DateField( field=models.DateField(
help_text="Duplication of registry's expirationdate saved for ease of reporting", help_text="Duplication of registry's expiration date saved for ease of reporting",
null=True, null=True,
), ),
), ),

View file

@ -27,16 +27,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="transitiondomain", model_name="transitiondomain",
name="organization_type", name="organization_type",
field=models.TextField( field=models.TextField(blank=True, help_text="Type of organization", max_length=255, null=True),
blank=True, help_text="Type of organization", max_length=255, null=True
),
), ),
migrations.AddField( migrations.AddField(
model_name="transitiondomain", model_name="transitiondomain",
name="organization_name", name="organization_name",
field=models.TextField( field=models.TextField(blank=True, db_index=True, help_text="Organization name", null=True),
blank=True, db_index=True, help_text="Organization name", null=True
),
), ),
migrations.AddField( migrations.AddField(
model_name="transitiondomain", model_name="transitiondomain",

View file

@ -262,10 +262,7 @@ class Domain(TimeStampedModel, DomainHelper):
doesn't add the created host to the domain doesn't add the created host to the domain
returns ErrorCode (int)""" returns ErrorCode (int)"""
if addrs is not None and addrs != []: if addrs is not None and addrs != []:
addresses = [ addresses = [epp.Ip(addr=addr, ip="v6" if self.is_ipv6(addr) else None) for addr in addrs]
epp.Ip(addr=addr, ip="v6" if self.is_ipv6(addr) else None)
for addr in addrs
]
request = commands.CreateHost(name=host, addrs=addresses) request = commands.CreateHost(name=host, addrs=addresses)
else: else:
request = commands.CreateHost(name=host) request = commands.CreateHost(name=host)
@ -358,15 +355,11 @@ class Domain(TimeStampedModel, DomainHelper):
raise NameserverError(code=nsErrorCodes.MISSING_IP, nameserver=nameserver) raise NameserverError(code=nsErrorCodes.MISSING_IP, nameserver=nameserver)
elif not cls.isSubdomain(name, nameserver) and (ip is not None and ip != []): elif not cls.isSubdomain(name, nameserver) and (ip is not None and ip != []):
raise NameserverError( raise NameserverError(code=nsErrorCodes.GLUE_RECORD_NOT_ALLOWED, nameserver=nameserver, ip=ip)
code=nsErrorCodes.GLUE_RECORD_NOT_ALLOWED, nameserver=nameserver, ip=ip
)
elif ip is not None and ip != []: elif ip is not None and ip != []:
for addr in ip: for addr in ip:
if not cls._valid_ip_addr(addr): if not cls._valid_ip_addr(addr):
raise NameserverError( raise NameserverError(code=nsErrorCodes.INVALID_IP, nameserver=nameserver[:40], ip=ip)
code=nsErrorCodes.INVALID_IP, nameserver=nameserver[:40], ip=ip
)
return None return None
@classmethod @classmethod
@ -382,9 +375,7 @@ class Domain(TimeStampedModel, DomainHelper):
except ValueError: except ValueError:
return False return False
def getNameserverChanges( def getNameserverChanges(self, hosts: list[tuple[str, list]]) -> tuple[list, list, dict, dict]:
self, hosts: list[tuple[str, list]]
) -> tuple[list, list, dict, dict]:
""" """
calls self.nameserver, it should pull from cache but may result calls self.nameserver, it should pull from cache but may result
in an epp call in an epp call
@ -420,40 +411,27 @@ class Domain(TimeStampedModel, DomainHelper):
else: else:
# TODO - host is being updated when previous was None+new is empty list # TODO - host is being updated when previous was None+new is empty list
# add check here # add check here
if newHostDict[prevHost] is not None and set( if newHostDict[prevHost] is not None and set(newHostDict[prevHost]) != set(addrs):
newHostDict[prevHost] self.__class__.checkHostIPCombo(name=self.name, nameserver=prevHost, ip=newHostDict[prevHost])
) != set(addrs):
self.__class__.checkHostIPCombo(
name=self.name, nameserver=prevHost, ip=newHostDict[prevHost]
)
updated_values.append((prevHost, newHostDict[prevHost])) updated_values.append((prevHost, newHostDict[prevHost]))
new_values = { new_values = {
key: newHostDict.get(key) key: newHostDict.get(key) for key in newHostDict if key not in previousHostDict and key.strip() != ""
for key in newHostDict
if key not in previousHostDict and key.strip() != ""
} }
for nameserver, ip in new_values.items(): for nameserver, ip in new_values.items():
self.__class__.checkHostIPCombo( self.__class__.checkHostIPCombo(name=self.name, nameserver=nameserver, ip=ip)
name=self.name, nameserver=nameserver, ip=ip
)
return (deleted_values, updated_values, new_values, previousHostDict) return (deleted_values, updated_values, new_values, previousHostDict)
def _update_host_values(self, updated_values, oldNameservers): def _update_host_values(self, updated_values, oldNameservers):
for hostTuple in updated_values: for hostTuple in updated_values:
updated_response_code = self._update_host( updated_response_code = self._update_host(hostTuple[0], hostTuple[1], oldNameservers.get(hostTuple[0]))
hostTuple[0], hostTuple[1], oldNameservers.get(hostTuple[0])
)
if updated_response_code not in [ if updated_response_code not in [
ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY, ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY,
ErrorCode.OBJECT_EXISTS, ErrorCode.OBJECT_EXISTS,
]: ]:
logger.warning( logger.warning("Could not update host %s. Error code was: %s " % (hostTuple[0], updated_response_code))
"Could not update host %s. Error code was: %s "
% (hostTuple[0], updated_response_code)
)
def createNewHostList(self, new_values: dict): def createNewHostList(self, new_values: dict):
"""convert the dictionary of new values to a list of HostObjSet """convert the dictionary of new values to a list of HostObjSet
@ -469,13 +447,8 @@ class Domain(TimeStampedModel, DomainHelper):
hostStringList = [] hostStringList = []
for key, value in new_values.items(): for key, value in new_values.items():
createdCode = self._create_host( createdCode = self._create_host(host=key, addrs=value) # creates in registry
host=key, addrs=value if createdCode == ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY or createdCode == ErrorCode.OBJECT_EXISTS:
) # creates in registry
if (
createdCode == ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY
or createdCode == ErrorCode.OBJECT_EXISTS
):
hostStringList.append(key) hostStringList.append(key)
if hostStringList == []: if hostStringList == []:
return [], 0 return [], 0
@ -522,9 +495,7 @@ class Domain(TimeStampedModel, DomainHelper):
logger.info("Domain does not have dnssec data defined %s" % err) logger.info("Domain does not have dnssec data defined %s" % err)
return None return None
def getDnssecdataChanges( def getDnssecdataChanges(self, _dnssecdata: Optional[extensions.DNSSECExtension]) -> tuple[dict, dict]:
self, _dnssecdata: Optional[extensions.DNSSECExtension]
) -> tuple[dict, dict]:
""" """
calls self.dnssecdata, it should pull from cache but may result calls self.dnssecdata, it should pull from cache but may result
in an epp call in an epp call
@ -550,20 +521,12 @@ class Domain(TimeStampedModel, DomainHelper):
if oldDnssecdata and len(oldDnssecdata.dsData) > 0: if oldDnssecdata and len(oldDnssecdata.dsData) > 0:
# if existing dsData not in new dsData, mark for removal # if existing dsData not in new dsData, mark for removal
dsDataForRemoval = [ dsDataForRemoval = [dsData for dsData in oldDnssecdata.dsData if dsData not in _dnssecdata.dsData]
dsData
for dsData in oldDnssecdata.dsData
if dsData not in _dnssecdata.dsData
]
if len(dsDataForRemoval) > 0: if len(dsDataForRemoval) > 0:
remDnssecdata["dsData"] = dsDataForRemoval remDnssecdata["dsData"] = dsDataForRemoval
# if new dsData not in existing dsData, mark for add # if new dsData not in existing dsData, mark for add
dsDataForAdd = [ dsDataForAdd = [dsData for dsData in _dnssecdata.dsData if dsData not in oldDnssecdata.dsData]
dsData
for dsData in _dnssecdata.dsData
if dsData not in oldDnssecdata.dsData
]
if len(dsDataForAdd) > 0: if len(dsDataForAdd) > 0:
addDnssecdata["dsData"] = dsDataForAdd addDnssecdata["dsData"] = dsDataForAdd
else: else:
@ -598,9 +561,7 @@ class Domain(TimeStampedModel, DomainHelper):
if "dsData" in _remDnssecdata and _remDnssecdata["dsData"] is not None: if "dsData" in _remDnssecdata and _remDnssecdata["dsData"] is not None:
registry.send(remRequest, cleaned=True) registry.send(remRequest, cleaned=True)
except RegistryError as e: except RegistryError as e:
logger.error( logger.error("Error updating DNSSEC, code was %s error was %s" % (e.code, e))
"Error updating DNSSEC, code was %s error was %s" % (e.code, e)
)
raise e raise e
@nameservers.setter # type: ignore @nameservers.setter # type: ignore
@ -630,14 +591,10 @@ class Domain(TimeStampedModel, DomainHelper):
oldNameservers, oldNameservers,
) = self.getNameserverChanges(hosts=hosts) ) = self.getNameserverChanges(hosts=hosts)
_ = self._update_host_values( _ = self._update_host_values(updated_values, oldNameservers) # returns nothing, just need to be run and errors
updated_values, oldNameservers
) # returns nothing, just need to be run and errors
addToDomainList, addToDomainCount = self.createNewHostList(new_values) addToDomainList, addToDomainCount = self.createNewHostList(new_values)
deleteHostList, deleteCount = self.createDeleteHostList(deleted_values) deleteHostList, deleteCount = self.createDeleteHostList(deleted_values)
responseCode = self.addAndRemoveHostsFromDomain( responseCode = self.addAndRemoveHostsFromDomain(hostsToAdd=addToDomainList, hostsToDelete=deleteHostList)
hostsToAdd=addToDomainList, hostsToDelete=deleteHostList
)
# if unable to update domain raise error and stop # if unable to update domain raise error and stop
if responseCode != ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY: if responseCode != ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY:
@ -651,19 +608,13 @@ class Domain(TimeStampedModel, DomainHelper):
self.dns_needed() self.dns_needed()
self.save() self.save()
except Exception as err: except Exception as err:
logger.info( logger.info("nameserver setter checked for dns_needed state and it did not succeed. Warning: %s" % err)
"nameserver setter checked for dns_needed state "
"and it did not succeed. Warning: %s" % err
)
elif successTotalNameservers >= 2 and successTotalNameservers <= 13: elif successTotalNameservers >= 2 and successTotalNameservers <= 13:
try: try:
self.ready() self.ready()
self.save() self.save()
except Exception as err: except Exception as err:
logger.info( logger.info("nameserver setter checked for create state and it did not succeed. Warning: %s" % err)
"nameserver setter checked for create state "
"and it did not succeed. Warning: %s" % err
)
@Cache @Cache
def statuses(self) -> list[str]: def statuses(self) -> list[str]:
@ -698,9 +649,7 @@ class Domain(TimeStampedModel, DomainHelper):
so follow on additions will update the current registrant""" so follow on additions will update the current registrant"""
logger.info("making registrant contact") logger.info("making registrant contact")
self._set_singleton_contact( self._set_singleton_contact(contact=contact, expectedType=contact.ContactTypeChoices.REGISTRANT)
contact=contact, expectedType=contact.ContactTypeChoices.REGISTRANT
)
@Cache @Cache
def administrative_contact(self) -> PublicContact | None: def administrative_contact(self) -> PublicContact | None:
@ -712,9 +661,7 @@ class Domain(TimeStampedModel, DomainHelper):
def administrative_contact(self, contact: PublicContact): def administrative_contact(self, contact: PublicContact):
logger.info("making admin contact") logger.info("making admin contact")
if contact.contact_type != contact.ContactTypeChoices.ADMINISTRATIVE: if contact.contact_type != contact.ContactTypeChoices.ADMINISTRATIVE:
raise ValueError( raise ValueError("Cannot set a registrant contact with a different contact type")
"Cannot set a registrant contact with a different contact type"
)
self._make_contact_in_registry(contact=contact) self._make_contact_in_registry(contact=contact)
self._update_domain_with_contact(contact, rem=False) self._update_domain_with_contact(contact, rem=False)
@ -736,20 +683,14 @@ class Domain(TimeStampedModel, DomainHelper):
try: try:
registry.send(updateContact, cleaned=True) registry.send(updateContact, cleaned=True)
except RegistryError as e: except RegistryError as e:
logger.error( logger.error("Error updating contact, code was %s error was %s" % (e.code, e))
"Error updating contact, code was %s error was %s" % (e.code, e)
)
# TODO - ticket 433 human readable error handling here # TODO - ticket 433 human readable error handling here
def _update_domain_with_contact(self, contact: PublicContact, rem=False): def _update_domain_with_contact(self, contact: PublicContact, rem=False):
"""adds or removes a contact from a domain """adds or removes a contact from a domain
rem being true indicates the contact will be removed from registry""" rem being true indicates the contact will be removed from registry"""
logger.info( logger.info("_update_domain_with_contact() received type %s " % contact.contact_type)
"_update_domain_with_contact() received type %s " % contact.contact_type domainContact = epp.DomainContact(contact=contact.registry_id, type=contact.contact_type)
)
domainContact = epp.DomainContact(
contact=contact.registry_id, type=contact.contact_type
)
updateDomain = commands.UpdateDomain(name=self.name, add=[domainContact]) updateDomain = commands.UpdateDomain(name=self.name, add=[domainContact])
if rem: if rem:
@ -758,17 +699,12 @@ class Domain(TimeStampedModel, DomainHelper):
try: try:
registry.send(updateDomain, cleaned=True) registry.send(updateDomain, cleaned=True)
except RegistryError as e: except RegistryError as e:
logger.error( logger.error("Error changing contact on a domain. Error code is %s error was %s" % (e.code, e))
"Error changing contact on a domain. Error code is %s error was %s"
% (e.code, e)
)
action = "add" action = "add"
if rem: if rem:
action = "remove" action = "remove"
raise Exception( raise Exception("Can't %s the contact of type %s" % (action, contact.contact_type))
"Can't %s the contact of type %s" % (action, contact.contact_type)
)
@Cache @Cache
def security_contact(self) -> PublicContact | None: def security_contact(self) -> PublicContact | None:
@ -778,16 +714,11 @@ class Domain(TimeStampedModel, DomainHelper):
def _add_registrant_to_existing_domain(self, contact: PublicContact): def _add_registrant_to_existing_domain(self, contact: PublicContact):
"""Used to change the registrant contact on an existing domain""" """Used to change the registrant contact on an existing domain"""
updateDomain = commands.UpdateDomain( updateDomain = commands.UpdateDomain(name=self.name, registrant=contact.registry_id)
name=self.name, registrant=contact.registry_id
)
try: try:
registry.send(updateDomain, cleaned=True) registry.send(updateDomain, cleaned=True)
except RegistryError as e: except RegistryError as e:
logger.error( logger.error("Error changing to new registrant error code is %s, error is %s" % (e.code, e))
"Error changing to new registrant error code is %s, error is %s"
% (e.code, e)
)
# TODO-error handling better here? # TODO-error handling better here?
def _set_singleton_contact(self, contact: PublicContact, expectedType: str): # noqa def _set_singleton_contact(self, contact: PublicContact, expectedType: str): # noqa
@ -800,16 +731,10 @@ class Domain(TimeStampedModel, DomainHelper):
Will throw error if contact type is not the same as expectType Will throw error if contact type is not the same as expectType
Raises ValueError if expected type doesn't match the contact type""" Raises ValueError if expected type doesn't match the contact type"""
if expectedType != contact.contact_type: if expectedType != contact.contact_type:
raise ValueError( raise ValueError("Cannot set a contact with a different contact type, expected type was %s" % expectedType)
"Cannot set a contact with a different contact type,"
" expected type was %s" % expectedType
)
isRegistrant = contact.contact_type == contact.ContactTypeChoices.REGISTRANT isRegistrant = contact.contact_type == contact.ContactTypeChoices.REGISTRANT
isEmptySecurity = ( isEmptySecurity = contact.contact_type == contact.ContactTypeChoices.SECURITY and contact.email == ""
contact.contact_type == contact.ContactTypeChoices.SECURITY
and contact.email == ""
)
# get publicContact objects that have the matching # get publicContact objects that have the matching
# domain and type but a different id # domain and type but a different id
@ -827,10 +752,7 @@ class Domain(TimeStampedModel, DomainHelper):
# contact is already added to the domain, but something may have changed on it # contact is already added to the domain, but something may have changed on it
alreadyExistsInRegistry = errorCode == ErrorCode.OBJECT_EXISTS alreadyExistsInRegistry = errorCode == ErrorCode.OBJECT_EXISTS
# if an error occured besides duplication, stop # if an error occured besides duplication, stop
if ( if not alreadyExistsInRegistry and errorCode != ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY:
not alreadyExistsInRegistry
and errorCode != ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY
):
# TODO- ticket #433 look here for error handling # TODO- ticket #433 look here for error handling
raise RegistryError(code=errorCode) raise RegistryError(code=errorCode)
@ -839,9 +761,7 @@ class Domain(TimeStampedModel, DomainHelper):
# if has conflicting contacts in our db remove them # if has conflicting contacts in our db remove them
if hasOtherContact: if hasOtherContact:
logger.info( logger.info("_set_singleton_contact()-> updating domain, removing old contact")
"_set_singleton_contact()-> updating domain, removing old contact"
)
existing_contact = ( existing_contact = (
PublicContact.objects.exclude(registry_id=contact.registry_id) PublicContact.objects.exclude(registry_id=contact.registry_id)
@ -859,9 +779,7 @@ class Domain(TimeStampedModel, DomainHelper):
self._update_domain_with_contact(contact=existing_contact, rem=True) self._update_domain_with_contact(contact=existing_contact, rem=True)
existing_contact.delete() existing_contact.delete()
except Exception as err: except Exception as err:
logger.error( logger.error("Raising error after removing and adding a new contact")
"Raising error after removing and adding a new contact"
)
raise (err) raise (err)
# update domain with contact or update the contact itself # update domain with contact or update the contact itself
@ -870,9 +788,7 @@ class Domain(TimeStampedModel, DomainHelper):
self._update_domain_with_contact(contact=contact, rem=False) self._update_domain_with_contact(contact=contact, rem=False)
# if already exists just update # if already exists just update
elif alreadyExistsInRegistry: elif alreadyExistsInRegistry:
current_contact = PublicContact.objects.filter( current_contact = PublicContact.objects.filter(registry_id=contact.registry_id).get()
registry_id=contact.registry_id
).get()
if current_contact.email != contact.email: if current_contact.email != contact.email:
self._update_epp_contact(contact=contact) self._update_epp_contact(contact=contact)
@ -880,9 +796,7 @@ class Domain(TimeStampedModel, DomainHelper):
logger.info("removing security contact and setting default again") logger.info("removing security contact and setting default again")
# get the current contact registry id for security # get the current contact registry id for security
current_contact = PublicContact.objects.filter( current_contact = PublicContact.objects.filter(registry_id=contact.registry_id).get()
registry_id=contact.registry_id
).get()
# don't let user delete the default without adding a new email # don't let user delete the default without adding a new email
if current_contact.email != PublicContact.get_default_security().email: if current_contact.email != PublicContact.get_default_security().email:
@ -900,9 +814,7 @@ class Domain(TimeStampedModel, DomainHelper):
from domain information (not domain application) from domain information (not domain application)
and should have the security email from DomainApplication""" and should have the security email from DomainApplication"""
logger.info("making security contact in registry") logger.info("making security contact in registry")
self._set_singleton_contact( self._set_singleton_contact(contact, expectedType=contact.ContactTypeChoices.SECURITY)
contact, expectedType=contact.ContactTypeChoices.SECURITY
)
@Cache @Cache
def technical_contact(self) -> PublicContact | None: def technical_contact(self) -> PublicContact | None:
@ -913,9 +825,7 @@ class Domain(TimeStampedModel, DomainHelper):
@technical_contact.setter # type: ignore @technical_contact.setter # type: ignore
def technical_contact(self, contact: PublicContact): def technical_contact(self, contact: PublicContact):
logger.info("making technical contact") logger.info("making technical contact")
self._set_singleton_contact( self._set_singleton_contact(contact, expectedType=contact.ContactTypeChoices.TECHNICAL)
contact, expectedType=contact.ContactTypeChoices.TECHNICAL
)
def is_active(self) -> bool: def is_active(self) -> bool:
"""Currently just returns if the state is created, """Currently just returns if the state is created,
@ -996,17 +906,13 @@ class Domain(TimeStampedModel, DomainHelper):
expiration_date = DateField( expiration_date = DateField(
null=True, null=True,
help_text=( help_text=("Duplication of registry's expiration date saved for ease of reporting"),
"Duplication of registry's expiration" "date saved for ease of reporting"
),
) )
def isActive(self): def isActive(self):
return self.state == Domain.State.CREATED return self.state == Domain.State.CREATED
def map_epp_contact_to_public_contact( def map_epp_contact_to_public_contact(self, contact: eppInfo.InfoContactResultData, contact_id, contact_type):
self, contact: eppInfo.InfoContactResultData, contact_id, contact_type
):
"""Maps the Epp contact representation to a PublicContact object. """Maps the Epp contact representation to a PublicContact object.
contact -> eppInfo.InfoContactResultData: The converted contact object contact -> eppInfo.InfoContactResultData: The converted contact object
@ -1028,10 +934,7 @@ class Domain(TimeStampedModel, DomainHelper):
# Since contact_id is registry_id, # Since contact_id is registry_id,
# check that its the right length # check that its the right length
contact_id_length = len(contact_id) contact_id_length = len(contact_id)
if ( if contact_id_length > PublicContact.get_max_id_length() or contact_id_length < 1:
contact_id_length > PublicContact.get_max_id_length()
or contact_id_length < 1
):
raise ContactError(code=ContactErrorCodes.CONTACT_ID_INVALID_LENGTH) raise ContactError(code=ContactErrorCodes.CONTACT_ID_INVALID_LENGTH)
if not isinstance(contact, eppInfo.InfoContactResultData): if not isinstance(contact, eppInfo.InfoContactResultData):
@ -1114,9 +1017,7 @@ class Domain(TimeStampedModel, DomainHelper):
) )
raise error raise error
def generic_contact_getter( def generic_contact_getter(self, contact_type_choice: PublicContact.ContactTypeChoices) -> PublicContact | None:
self, contact_type_choice: PublicContact.ContactTypeChoices
) -> PublicContact | None:
"""Retrieves the desired PublicContact from the registry. """Retrieves the desired PublicContact from the registry.
This abstracts the caching and EPP retrieval for This abstracts the caching and EPP retrieval for
all contact items and thus may result in EPP calls being sent. all contact items and thus may result in EPP calls being sent.
@ -1187,9 +1088,7 @@ class Domain(TimeStampedModel, DomainHelper):
if contact_type == PublicContact.ContactTypeChoices.REGISTRANT: if contact_type == PublicContact.ContactTypeChoices.REGISTRANT:
desired_contact = None desired_contact = None
if isinstance(contacts, str): if isinstance(contacts, str):
desired_contact = self._registrant_to_public_contact( desired_contact = self._registrant_to_public_contact(self._cache["registrant"])
self._cache["registrant"]
)
# Set the cache with the updated object # Set the cache with the updated object
# for performance reasons. # for performance reasons.
if "registrant" in self._cache: if "registrant" in self._cache:
@ -1203,9 +1102,7 @@ class Domain(TimeStampedModel, DomainHelper):
if contacts is not None and contact_type in contacts: if contacts is not None and contact_type in contacts:
_registry_id = contacts.get(contact_type) _registry_id = contacts.get(contact_type)
desired = PublicContact.objects.filter( desired = PublicContact.objects.filter(registry_id=_registry_id, domain=self, contact_type=contact_type)
registry_id=_registry_id, domain=self, contact_type=contact_type
)
if desired.count() == 1: if desired.count() == 1:
return desired.get() return desired.get()
@ -1214,10 +1111,7 @@ class Domain(TimeStampedModel, DomainHelper):
return None return None
def _handle_registrant_contact(self, contact): def _handle_registrant_contact(self, contact):
if ( if contact.contact_type is not None and contact.contact_type == PublicContact.ContactTypeChoices.REGISTRANT:
contact.contact_type is not None
and contact.contact_type == PublicContact.ContactTypeChoices.REGISTRANT
):
return contact return contact
else: else:
raise ValueError("Invalid contact object for registrant_contact") raise ValueError("Invalid contact object for registrant_contact")
@ -1297,9 +1191,7 @@ class Domain(TimeStampedModel, DomainHelper):
administrative_contact = self.get_default_administrative_contact() administrative_contact = self.get_default_administrative_contact()
administrative_contact.save() administrative_contact.save()
@transition( @transition(field="state", source=[State.READY, State.ON_HOLD], target=State.ON_HOLD)
field="state", source=[State.READY, State.ON_HOLD], target=State.ON_HOLD
)
def place_client_hold(self, ignoreEPP=False): def place_client_hold(self, ignoreEPP=False):
"""place a clienthold on a domain (no longer should resolve) """place a clienthold on a domain (no longer should resolve)
ignoreEPP (boolean) - set to true to by-pass EPP (used for transition domains) ignoreEPP (boolean) - set to true to by-pass EPP (used for transition domains)
@ -1325,9 +1217,7 @@ class Domain(TimeStampedModel, DomainHelper):
self._remove_client_hold() self._remove_client_hold()
# TODO -on the client hold ticket any additional error handling here # TODO -on the client hold ticket any additional error handling here
@transition( @transition(field="state", source=[State.ON_HOLD, State.DNS_NEEDED], target=State.DELETED)
field="state", source=[State.ON_HOLD, State.DNS_NEEDED], target=State.DELETED
)
def deletedInEpp(self): def deletedInEpp(self):
"""Domain is deleted in epp but is saved in our database. """Domain is deleted in epp but is saved in our database.
Error handling should be provided by the caller.""" Error handling should be provided by the caller."""
@ -1345,9 +1235,7 @@ class Domain(TimeStampedModel, DomainHelper):
logger.error("Could not delete domain. FSM failure: {err}") logger.error("Could not delete domain. FSM failure: {err}")
raise err raise err
except Exception as err: except Exception as err:
logger.error( logger.error(f"Could not delete domain. An unspecified error occured: {err}")
f"Could not delete domain. An unspecified error occured: {err}"
)
raise err raise err
else: else:
self._invalidate_cache() self._invalidate_cache()
@ -1407,9 +1295,7 @@ class Domain(TimeStampedModel, DomainHelper):
is_security = contact.contact_type == contact.ContactTypeChoices.SECURITY is_security = contact.contact_type == contact.ContactTypeChoices.SECURITY
DF = epp.DiscloseField DF = epp.DiscloseField
fields = {DF.EMAIL} fields = {DF.EMAIL}
disclose = ( disclose = is_security and contact.email != PublicContact.get_default_security().email
is_security and contact.email != PublicContact.get_default_security().email
)
# Will only disclose DF.EMAIL if its not the default # Will only disclose DF.EMAIL if its not the default
return epp.Disclose( return epp.Disclose(
flag=disclose, flag=disclose,
@ -1421,9 +1307,7 @@ class Domain(TimeStampedModel, DomainHelper):
name=contact.name, name=contact.name,
addr=epp.ContactAddr( addr=epp.ContactAddr(
street=[ street=[
getattr(contact, street) getattr(contact, street) for street in ["street1", "street2", "street3"] if hasattr(contact, street)
for street in ["street1", "street2", "street3"]
if hasattr(contact, street)
], # type: ignore ], # type: ignore
city=contact.city, city=contact.city,
pc=contact.pc, pc=contact.pc,
@ -1488,9 +1372,7 @@ class Domain(TimeStampedModel, DomainHelper):
data = registry.send(req, cleaned=True).res_data[0] data = registry.send(req, cleaned=True).res_data[0]
# Map the object we recieved from EPP to a PublicContact # Map the object we recieved from EPP to a PublicContact
mapped_object = self.map_epp_contact_to_public_contact( mapped_object = self.map_epp_contact_to_public_contact(data, domainContact.contact, domainContact.type)
data, domainContact.contact, domainContact.type
)
# Find/create it in the DB # Find/create it in the DB
in_db = self._get_or_create_public_contact(mapped_object) in_db = self._get_or_create_public_contact(mapped_object)
@ -1503,9 +1385,7 @@ class Domain(TimeStampedModel, DomainHelper):
return self._request_contact_info(contact) return self._request_contact_info(contact)
except RegistryError as e: except RegistryError as e:
if e.code == ErrorCode.OBJECT_DOES_NOT_EXIST: if e.code == ErrorCode.OBJECT_DOES_NOT_EXIST:
logger.info( logger.info("_get_or_create_contact()-> contact doesn't exist so making it")
"_get_or_create_contact()-> contact doesn't exist so making it"
)
contact.domain = self contact.domain = self
contact.save() # this will call the function based on type of contact contact.save() # this will call the function based on type of contact
return self._request_contact_info(contact=contact) return self._request_contact_info(contact=contact)
@ -1560,9 +1440,7 @@ class Domain(TimeStampedModel, DomainHelper):
return [] return []
for ip_addr in ip_list: for ip_addr in ip_list:
edited_ip_list.append( edited_ip_list.append(epp.Ip(addr=ip_addr, ip="v6" if self.is_ipv6(ip_addr) else None))
epp.Ip(addr=ip_addr, ip="v6" if self.is_ipv6(ip_addr) else None)
)
return edited_ip_list return edited_ip_list
@ -1579,12 +1457,7 @@ class Domain(TimeStampedModel, DomainHelper):
""" """
try: try:
if ( if ip_list is None or len(ip_list) == 0 and isinstance(old_ip_list, list) and len(old_ip_list) != 0:
ip_list is None
or len(ip_list) == 0
and isinstance(old_ip_list, list)
and len(old_ip_list) != 0
):
return ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY return ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY
added_ip_list = set(ip_list).difference(old_ip_list) added_ip_list = set(ip_list).difference(old_ip_list)
@ -1607,9 +1480,7 @@ class Domain(TimeStampedModel, DomainHelper):
else: else:
raise e raise e
def addAndRemoveHostsFromDomain( def addAndRemoveHostsFromDomain(self, hostsToAdd: list[str], hostsToDelete: list[str]):
self, hostsToAdd: list[str], hostsToDelete: list[str]
):
"""sends an UpdateDomain message to the registry with the hosts provided """sends an UpdateDomain message to the registry with the hosts provided
Args: Args:
hostsToDelete (list[epp.HostObjSet])- list of host objects to delete hostsToDelete (list[epp.HostObjSet])- list of host objects to delete
@ -1624,22 +1495,14 @@ class Domain(TimeStampedModel, DomainHelper):
return ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY return ErrorCode.COMMAND_COMPLETED_SUCCESSFULLY
try: try:
updateReq = commands.UpdateDomain( updateReq = commands.UpdateDomain(name=self.name, rem=hostsToDelete, add=hostsToAdd)
name=self.name, rem=hostsToDelete, add=hostsToAdd
)
logger.info( logger.info("addAndRemoveHostsFromDomain()-> sending update domain req as %s" % updateReq)
"addAndRemoveHostsFromDomain()-> sending update domain req as %s"
% updateReq
)
response = registry.send(updateReq, cleaned=True) response = registry.send(updateReq, cleaned=True)
return response.code return response.code
except RegistryError as e: except RegistryError as e:
logger.error( logger.error("Error addAndRemoveHostsFromDomain, code was %s error was %s" % (e.code, e))
"Error addAndRemoveHostsFromDomain, code was %s error was %s"
% (e.code, e)
)
return e.code return e.code
def _delete_hosts_if_not_used(self, hostsToDelete: list[str]): def _delete_hosts_if_not_used(self, hostsToDelete: list[str]):
@ -1657,22 +1520,13 @@ class Domain(TimeStampedModel, DomainHelper):
for nameserver in hostsToDelete: for nameserver in hostsToDelete:
deleteHostReq = commands.DeleteHost(name=nameserver) deleteHostReq = commands.DeleteHost(name=nameserver)
registry.send(deleteHostReq, cleaned=True) registry.send(deleteHostReq, cleaned=True)
logger.info( logger.info("_delete_hosts_if_not_used()-> sending delete host req as %s" % deleteHostReq)
"_delete_hosts_if_not_used()-> sending delete host req as %s"
% deleteHostReq
)
except RegistryError as e: except RegistryError as e:
if e.code == ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION: if e.code == ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION:
logger.info( logger.info("Did not remove host %s because it is in use on another domain." % nameserver)
"Did not remove host %s because it is in use on another domain."
% nameserver
)
else: else:
logger.error( logger.error("Error _delete_hosts_if_not_used, code was %s error was %s" % (e.code, e))
"Error _delete_hosts_if_not_used, code was %s error was %s"
% (e.code, e)
)
def _fetch_cache(self, fetch_hosts=False, fetch_contacts=False): def _fetch_cache(self, fetch_hosts=False, fetch_contacts=False):
"""Contact registry for info about a domain.""" """Contact registry for info about a domain."""
@ -1768,9 +1622,7 @@ class Domain(TimeStampedModel, DomainHelper):
# Raise an error if we find duplicates. # Raise an error if we find duplicates.
# This should not occur # This should not occur
if db_contact.count() > 1: if db_contact.count() > 1:
raise Exception( raise Exception(f"Multiple contacts found for {public_contact.contact_type}")
f"Multiple contacts found for {public_contact.contact_type}"
)
# Save to DB if it doesn't exist already. # Save to DB if it doesn't exist already.
if db_contact.count() == 0: if db_contact.count() == 0:
@ -1784,10 +1636,7 @@ class Domain(TimeStampedModel, DomainHelper):
# Does the item we're grabbing match # Does the item we're grabbing match
# what we have in our DB? # what we have in our DB?
if ( if existing_contact.email != public_contact.email or existing_contact.registry_id != public_contact.registry_id:
existing_contact.email != public_contact.email
or existing_contact.registry_id != public_contact.registry_id
):
existing_contact.delete() existing_contact.delete()
public_contact.save() public_contact.save()
logger.warning("Requested PublicContact is out of sync " "with DB.") logger.warning("Requested PublicContact is out of sync " "with DB.")
@ -1809,9 +1658,7 @@ class Domain(TimeStampedModel, DomainHelper):
# Grabs the expanded contact # Grabs the expanded contact
full_object = self._request_contact_info(contact) full_object = self._request_contact_info(contact)
# Maps it to type PublicContact # Maps it to type PublicContact
mapped_object = self.map_epp_contact_to_public_contact( mapped_object = self.map_epp_contact_to_public_contact(full_object, contact.registry_id, contact.contact_type)
full_object, contact.registry_id, contact.contact_type
)
return self._get_or_create_public_contact(mapped_object) return self._get_or_create_public_contact(mapped_object)
def _invalidate_cache(self): def _invalidate_cache(self):
@ -1829,6 +1676,4 @@ class Domain(TimeStampedModel, DomainHelper):
if property in self._cache: if property in self._cache:
return self._cache[property] return self._cache[property]
else: else:
raise KeyError( raise KeyError("Requested key %s was not found in registry cache." % str(property))
"Requested key %s was not found in registry cache." % str(property)
)

View file

@ -131,8 +131,7 @@ class DomainApplication(TimeStampedModel):
FEDERAL = ( FEDERAL = (
"federal", "federal",
"Federal: an agency of the U.S. government's executive, " "Federal: an agency of the U.S. government's executive, legislative, or judicial branches",
"legislative, or judicial branches",
) )
INTERSTATE = "interstate", "Interstate: an organization of two or more states" INTERSTATE = "interstate", "Interstate: an organization of two or more states"
STATE_OR_TERRITORY = ( STATE_OR_TERRITORY = (
@ -143,8 +142,7 @@ class DomainApplication(TimeStampedModel):
) )
TRIBAL = ( TRIBAL = (
"tribal", "tribal",
"Tribal: a tribal government recognized by the federal or a state " "Tribal: a tribal government recognized by the federal or a state government",
"government",
) )
COUNTY = "county", "County: a county, parish, or borough" COUNTY = "county", "County: a county, parish, or borough"
CITY = "city", "City: a city, town, township, village, etc." CITY = "city", "City: a city, town, township, village, etc."
@ -154,8 +152,7 @@ class DomainApplication(TimeStampedModel):
) )
SCHOOL_DISTRICT = ( SCHOOL_DISTRICT = (
"school_district", "school_district",
"School district: a school district that is not part of a local " "School district: a school district that is not part of a local government",
"government",
) )
class BranchChoices(models.TextChoices): class BranchChoices(models.TextChoices):
@ -169,10 +166,7 @@ class DomainApplication(TimeStampedModel):
"American Battle Monuments Commission", "American Battle Monuments Commission",
"AMTRAK", "AMTRAK",
"Appalachian Regional Commission", "Appalachian Regional Commission",
( ("Appraisal Subcommittee of the Federal Financial " "Institutions Examination Council"),
"Appraisal Subcommittee of the Federal Financial "
"Institutions Examination Council"
),
"Appraisal Subcommittee", "Appraisal Subcommittee",
"Architect of the Capitol", "Architect of the Capitol",
"Armed Forces Retirement Home", "Armed Forces Retirement Home",
@ -572,9 +566,7 @@ class DomainApplication(TimeStampedModel):
return not self.approved_domain.is_active() return not self.approved_domain.is_active()
return True return True
def _send_status_update_email( def _send_status_update_email(self, new_status, email_template, email_template_subject):
self, new_status, email_template, email_template_subject
):
"""Send a atatus update email to the submitter. """Send a atatus update email to the submitter.
The email goes to the email address that the submitter gave as their The email goes to the email address that the submitter gave as their
@ -583,9 +575,7 @@ class DomainApplication(TimeStampedModel):
""" """
if self.submitter is None or self.submitter.email is None: if self.submitter is None or self.submitter.email is None:
logger.warning( logger.warning(f"Cannot send {new_status} email, no submitter email address.")
f"Cannot send {new_status} email, no submitter email address."
)
return return
try: try:
send_templated_email( send_templated_email(
@ -598,9 +588,7 @@ class DomainApplication(TimeStampedModel):
except EmailSendingError: except EmailSendingError:
logger.warning("Failed to send confirmation email", exc_info=True) logger.warning("Failed to send confirmation email", exc_info=True)
@transition( @transition(field="status", source=[STARTED, ACTION_NEEDED, WITHDRAWN], target=SUBMITTED)
field="status", source=[STARTED, ACTION_NEEDED, WITHDRAWN], target=SUBMITTED
)
def submit(self): def submit(self):
"""Submit an application that is started. """Submit an application that is started.

View file

@ -54,6 +54,7 @@ class DomainInformation(TimeStampedModel):
blank=True, blank=True,
help_text="Type of Organization", help_text="Type of Organization",
) )
federally_recognized_tribe = models.BooleanField( federally_recognized_tribe = models.BooleanField(
null=True, null=True,
help_text="Is the tribe federally recognized", help_text="Is the tribe federally recognized",

View file

@ -57,9 +57,7 @@ class DomainInvitation(TimeStampedModel):
except User.DoesNotExist: except User.DoesNotExist:
# should not happen because a matching user should exist before # should not happen because a matching user should exist before
# we retrieve this invitation # we retrieve this invitation
raise RuntimeError( raise RuntimeError("Cannot find the user to retrieve this domain invitation.")
"Cannot find the user to retrieve this domain invitation."
)
# and create a role for that user on this domain # and create a role for that user on this domain
_, created = UserDomainRole.objects.get_or_create( _, created = UserDomainRole.objects.get_or_create(
@ -68,6 +66,4 @@ class DomainInvitation(TimeStampedModel):
if not created: if not created:
# something strange happened and this role already existed when # something strange happened and this role already existed when
# the invitation was retrieved. Log that this occurred. # the invitation was retrieved. Log that this occurred.
logger.warn( logger.warn("Invitation %s was retrieved for a role that already exists.", self)
"Invitation %s was retrieved for a role that already exists.", self
)

View file

@ -10,9 +10,7 @@ from .utility.time_stamped_model import TimeStampedModel
def get_id(): def get_id():
"""Generate a 16 character registry ID with a low probability of collision.""" """Generate a 16 character registry ID with a low probability of collision."""
day = datetime.today().strftime("%A")[:2] day = datetime.today().strftime("%A")[:2]
rand = "".join( rand = "".join(choices(ascii_uppercase + ascii_lowercase + digits, k=14)) # nosec B311
choices(ascii_uppercase + ascii_lowercase + digits, k=14) # nosec B311
)
return f"{day}{rand}" return f"{day}{rand}"
@ -69,16 +67,12 @@ class PublicContact(TimeStampedModel):
pc = models.TextField(null=False, help_text="Contact's postal code") pc = models.TextField(null=False, help_text="Contact's postal code")
cc = models.TextField(null=False, help_text="Contact's country code") cc = models.TextField(null=False, help_text="Contact's country code")
email = models.TextField(null=False, help_text="Contact's email address") email = models.TextField(null=False, help_text="Contact's email address")
voice = models.TextField( voice = models.TextField(null=False, help_text="Contact's phone number. Must be in ITU.E164.2005 format")
null=False, help_text="Contact's phone number. Must be in ITU.E164.2005 format"
)
fax = models.TextField( fax = models.TextField(
null=True, null=True,
help_text="Contact's fax number (null ok). Must be in ITU.E164.2005 format.", help_text="Contact's fax number (null ok). Must be in ITU.E164.2005 format.",
) )
pw = models.TextField( pw = models.TextField(null=False, help_text="Contact's authorization code. 16 characters minimum.")
null=False, help_text="Contact's authorization code. 16 characters minimum."
)
@classmethod @classmethod
def get_default_registrant(cls): def get_default_registrant(cls):
@ -154,8 +148,4 @@ class PublicContact(TimeStampedModel):
return cls._meta.get_field("registry_id").max_length return cls._meta.get_field("registry_id").max_length
def __str__(self): def __str__(self):
return ( return f"{self.name} <{self.email}>" f"id: {self.registry_id} " f"type: {self.contact_type}"
f"{self.name} <{self.email}>"
f"id: {self.registry_id} "
f"type: {self.contact_type}"
)

View file

@ -66,15 +66,11 @@ class TransitionDomain(TimeStampedModel):
) )
epp_creation_date = models.DateField( epp_creation_date = models.DateField(
null=True, null=True,
help_text=( help_text=("Duplication of registry's creation " "date saved for ease of reporting"),
"Duplication of registry's creation " "date saved for ease of reporting"
),
) )
epp_expiration_date = models.DateField( epp_expiration_date = models.DateField(
null=True, null=True,
help_text=( help_text=("Duplication of registry's expiration " "date saved for ease of reporting"),
"Duplication of registry's expiration " "date saved for ease of reporting"
),
) )
first_name = models.TextField( first_name = models.TextField(
null=True, null=True,

View file

@ -68,9 +68,7 @@ class User(AbstractUser):
def check_domain_invitations_on_login(self): def check_domain_invitations_on_login(self):
"""When a user first arrives on the site, we need to retrieve any domain """When a user first arrives on the site, we need to retrieve any domain
invitations that match their email address.""" invitations that match their email address."""
for invitation in DomainInvitation.objects.filter( for invitation in DomainInvitation.objects.filter(email=self.email, status=DomainInvitation.INVITED):
email=self.email, status=DomainInvitation.INVITED
):
try: try:
invitation.retrieve() invitation.retrieve()
invitation.save() invitation.save()
@ -78,9 +76,7 @@ class User(AbstractUser):
# retrieving should not fail because of a missing user, but # retrieving should not fail because of a missing user, but
# if it does fail, log the error so a new user can continue # if it does fail, log the error so a new user can continue
# logging in # logging in
logger.warn( logger.warn("Failed to retrieve invitation %s", invitation, exc_info=True)
"Failed to retrieve invitation %s", invitation, exc_info=True
)
def create_domain_and_invite(self, transition_domain: TransitionDomain): def create_domain_and_invite(self, transition_domain: TransitionDomain):
transition_domain_name = transition_domain.domain_name transition_domain_name = transition_domain.domain_name
@ -89,9 +85,7 @@ class User(AbstractUser):
# type safety check. name should never be none # type safety check. name should never be none
if transition_domain_name is not None: if transition_domain_name is not None:
new_domain = Domain( new_domain = Domain(name=transition_domain_name, state=transition_domain_status)
name=transition_domain_name, state=transition_domain_status
)
new_domain.save() new_domain.save()
# check that a domain invitation doesn't already # check that a domain invitation doesn't already
# exist for this e-mail / Domain pair # exist for this e-mail / Domain pair
@ -100,9 +94,7 @@ class User(AbstractUser):
).exists() ).exists()
if not domain_email_already_in_domain_invites: if not domain_email_already_in_domain_invites:
# Create new domain invitation # Create new domain invitation
new_domain_invitation = DomainInvitation( new_domain_invitation = DomainInvitation(email=transition_domain_email.lower(), domain=new_domain)
email=transition_domain_email.lower(), domain=new_domain
)
new_domain_invitation.save() new_domain_invitation.save()
def check_transition_domains_on_login(self): def check_transition_domains_on_login(self):
@ -129,9 +121,7 @@ class User(AbstractUser):
# with our data and migrations need to be run again. # with our data and migrations need to be run again.
# Get the domain that corresponds with this transition domain # Get the domain that corresponds with this transition domain
domain_exists = Domain.objects.filter( domain_exists = Domain.objects.filter(name=transition_domain.domain_name).exists()
name=transition_domain.domain_name
).exists()
if not domain_exists: if not domain_exists:
logger.warn( logger.warn(
"""There are transition domains without """There are transition domains without
@ -147,17 +137,15 @@ class User(AbstractUser):
# Create a domain information object, if one doesn't # Create a domain information object, if one doesn't
# already exist # already exist
domain_info_exists = DomainInformation.objects.filter( domain_info_exists = DomainInformation.objects.filter(domain=domain).exists()
domain=domain
).exists()
if not domain_info_exists: if not domain_info_exists:
new_domain_info = DomainInformation(creator=self, domain=domain) new_domain_info = DomainInformation(creator=self, domain=domain)
new_domain_info.save() new_domain_info.save()
def first_login(self): def on_each_login(self):
"""Callback when the user is authenticated for the very first time. """Callback each time the user is authenticated.
When a user first arrives on the site, we need to retrieve any domain When a user arrives on the site each time, we need to retrieve any domain
invitations that match their email address. invitations that match their email address.
We also need to check if they are logging in with the same e-mail We also need to check if they are logging in with the same e-mail

View file

@ -44,7 +44,5 @@ class UserDomainRole(TimeStampedModel):
constraints = [ constraints = [
# a user can have only one role on a given domain, that is, there can # a user can have only one role on a given domain, that is, there can
# be only a single row with a certain (user, domain) pair. # be only a single row with a certain (user, domain) pair.
models.UniqueConstraint( models.UniqueConstraint(fields=["user", "domain"], name="unique_user_domain_role")
fields=["user", "domain"], name="unique_user_domain_role"
)
] ]

View file

@ -87,14 +87,10 @@ class UserGroup(Group):
permissions = permission["permissions"] permissions = permission["permissions"]
# Retrieve the content type for the app and model # Retrieve the content type for the app and model
content_type = ContentType.objects.get( content_type = ContentType.objects.get(app_label=app_label, model=model_name)
app_label=app_label, model=model_name
)
# Retrieve the permissions based on their codenames # Retrieve the permissions based on their codenames
permissions = Permission.objects.filter( permissions = Permission.objects.filter(content_type=content_type, codename__in=permissions)
content_type=content_type, codename__in=permissions
)
# Assign the permissions to the group # Assign the permissions to the group
cisa_analysts_group.permissions.add(*permissions) cisa_analysts_group.permissions.add(*permissions)
@ -113,9 +109,7 @@ class UserGroup(Group):
) )
cisa_analysts_group.save() cisa_analysts_group.save()
logger.debug( logger.debug("CISA Analyt permissions added to group " + cisa_analysts_group.name)
"CISA Analyt permissions added to group " + cisa_analysts_group.name
)
except Exception as e: except Exception as e:
logger.error(f"Error creating analyst permissions group: {e}") logger.error(f"Error creating analyst permissions group: {e}")

View file

@ -3,6 +3,14 @@
<html class="no-js" lang="{{ LANGUAGE_CODE }}"> <html class="no-js" lang="{{ LANGUAGE_CODE }}">
<head> <head>
{% if IS_PRODUCTION %}
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-PZ5QSP6QPL"></script>
<script>
window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-PZ5QSP6QPL');
</script>
{% endif %}
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge"> <meta http-equiv="x-ua-compatible" content="ie=edge">
<title> <title>

View file

@ -8,15 +8,19 @@
{% block field_sets %} {% block field_sets %}
<div class="submit-row"> <div class="submit-row">
{% if original.state == original.State.READY %}
<input type="submit" value="Place hold" name="_place_client_hold">
{% elif original.state == original.State.ON_HOLD %}
<input type="submit" value="Remove hold" name="_remove_client_hold">
{% endif %}
<input id="manageDomainSubmitButton" type="submit" value="Manage domain" name="_edit_domain"> <input id="manageDomainSubmitButton" type="submit" value="Manage domain" name="_edit_domain">
<input type="submit" value="Get registry status" name="_get_status"> <input type="submit" value="Get registry status" name="_get_status">
<div class="spacer"></div>
{% if original.state == original.State.READY %}
<input type="submit" value="Place hold" name="_place_client_hold" class="custom-link-button">
{% elif original.state == original.State.ON_HOLD %}
<input type="submit" value="Remove hold" name="_remove_client_hold" class="custom-link-button">
{% endif %}
{% if original.state == original.State.READY or original.state == original.State.ON_HOLD %}
<span> | </span>
{% endif %}
{% if original.state != original.State.DELETED %} {% if original.state != original.State.DELETED %}
<input type="submit" value="Delete domain in registry" name="_delete_domain"> <input type="submit" value="Remove from registry" name="_delete_domain" class="custom-link-button">
{% endif %} {% endif %}
</div> </div>
{{ block.super }} {{ block.super }}

View file

@ -25,7 +25,7 @@ This is a good time to check who has access to your .gov domain{% if domains|len
2. Click the “Manage” link next to your .gov domain, then click on “Domain managers” to see who has access to your domain. 2. Click the “Manage” link next to your .gov domain, then click on “Domain managers” to see who has access to your domain.
3. If any of these users should not have access to your domain, let us know in a reply to this email. 3. If any of these users should not have access to your domain, let us know in a reply to this email.
After verifying who has access to your domain{% if domains|length > 1 %}s{% endif %}, we also suggest reviewing your contact information and organization mailing address to ensure those are up to date. After verifying who has access to your domain{% if domains|length > 1 %}s{% endif %}, review your contact information to make sure it's up to date.
DOMAIN EXPIRATION DATES EXTENDED BY ONE YEAR DOMAIN EXPIRATION DATES EXTENDED BY ONE YEAR

View file

@ -13,7 +13,7 @@
<h1>Manage your domains</h2> <h1>Manage your domains</h2>
<p class="margin-top-4"> <p class="margin-top-4">
{% if is_production %} {% if IS_PRODUCTION %}
<a href="javascript:void(0)" <a href="javascript:void(0)"
class="usa-button usa-tooltip disabled-link" class="usa-button usa-tooltip disabled-link"
data-position="right" data-position="right"
@ -139,7 +139,7 @@
></div> ></div>
{% else %} {% else %}
<p>You don't have any active domain requests right now</p> <p>You don't have any active domain requests right now</p>
<p><a href="{% url 'application:' %}" class="usa-button">Start a new domain request</a></p> <!-- <p><a href="{% url 'application:' %}" class="usa-button">Start a new domain request</a></p> -->
{% endif %} {% endif %}
</section> </section>

View file

@ -55,9 +55,7 @@ def contains_checkbox(html_list):
@register.filter @register.filter
def get_organization_long_name(organization_type): def get_organization_long_name(organization_type):
organization_choices_dict = dict( organization_choices_dict = dict(DomainApplication.OrganizationChoicesVerbose.choices)
DomainApplication.OrganizationChoicesVerbose.choices
)
long_form_type = organization_choices_dict[organization_type] long_form_type = organization_choices_dict[organization_type]
if long_form_type is None: if long_form_type is None:
logger.error("Organization type error, triggered by a template's custom filter") logger.error("Organization type error, triggered by a template's custom filter")

View file

@ -58,7 +58,7 @@ def get_handlers():
@contextmanager @contextmanager
def less_console_noise(): def less_console_noise(output_stream=None):
""" """
Context manager to use in tests to silence console logging. Context manager to use in tests to silence console logging.
@ -66,14 +66,19 @@ def less_console_noise():
(such as errors) which are normal and expected. (such as errors) which are normal and expected.
It can easily be removed to debug a failing test. It can easily be removed to debug a failing test.
Arguments:
`output_stream`: a stream to redirect every handler to. If it's
not provided, use /dev/null.
""" """
restore = {} restore = {}
handlers = get_handlers() handlers = get_handlers()
devnull = open(os.devnull, "w") if output_stream is None:
output_stream = open(os.devnull, "w")
# redirect all the streams # redirect all the streams
for handler in handlers.values(): for handler in handlers.values():
prior = handler.setStream(devnull) prior = handler.setStream(output_stream)
restore[handler.name] = prior restore[handler.name] = prior
try: try:
# run the test # run the test
@ -82,8 +87,9 @@ def less_console_noise():
# restore the streams # restore the streams
for handler in handlers.values(): for handler in handlers.values():
handler.setStream(restore[handler.name]) handler.setStream(restore[handler.name])
# close the file we opened if output_stream is None:
devnull.close() # we opened output_stream so we have to close it
output_stream.close()
class MockUserLogin: class MockUserLogin:
@ -317,9 +323,7 @@ class AuditedAdminMockData:
of either DomainApplication, DomainInvitation, or DomainInformation of either DomainApplication, DomainInvitation, or DomainInformation
based on the 'domain_type' field. based on the 'domain_type' field.
""" # noqa """ # noqa
common_args = self.get_common_domain_arg_dictionary( common_args = self.get_common_domain_arg_dictionary(item_name, org_type, federal_type, purpose)
item_name, org_type, federal_type, purpose
)
full_arg_dict = None full_arg_dict = None
match domain_type: match domain_type:
case self.APPLICATION: case self.APPLICATION:
@ -344,40 +348,22 @@ class AuditedAdminMockData:
) )
return full_arg_dict return full_arg_dict
def create_full_dummy_domain_application( def create_full_dummy_domain_application(self, item_name, status=DomainApplication.STARTED):
self, item_name, status=DomainApplication.STARTED
):
"""Creates a dummy domain application object""" """Creates a dummy domain application object"""
domain_application_kwargs = self.dummy_kwarg_boilerplate( domain_application_kwargs = self.dummy_kwarg_boilerplate(self.APPLICATION, item_name, status)
self.APPLICATION, item_name, status application = DomainApplication.objects.get_or_create(**domain_application_kwargs)[0]
)
application = DomainApplication.objects.get_or_create(
**domain_application_kwargs
)[0]
return application return application
def create_full_dummy_domain_information( def create_full_dummy_domain_information(self, item_name, status=DomainApplication.STARTED):
self, item_name, status=DomainApplication.STARTED
):
"""Creates a dummy domain information object""" """Creates a dummy domain information object"""
domain_application_kwargs = self.dummy_kwarg_boilerplate( domain_application_kwargs = self.dummy_kwarg_boilerplate(self.INFORMATION, item_name, status)
self.INFORMATION, item_name, status application = DomainInformation.objects.get_or_create(**domain_application_kwargs)[0]
)
application = DomainInformation.objects.get_or_create(
**domain_application_kwargs
)[0]
return application return application
def create_full_dummy_domain_invitation( def create_full_dummy_domain_invitation(self, item_name, status=DomainApplication.STARTED):
self, item_name, status=DomainApplication.STARTED
):
"""Creates a dummy domain invitation object""" """Creates a dummy domain invitation object"""
domain_application_kwargs = self.dummy_kwarg_boilerplate( domain_application_kwargs = self.dummy_kwarg_boilerplate(self.INVITATION, item_name, status)
self.INVITATION, item_name, status application = DomainInvitation.objects.get_or_create(**domain_application_kwargs)[0]
)
application = DomainInvitation.objects.get_or_create(
**domain_application_kwargs
)[0]
return application return application
@ -394,17 +380,11 @@ class AuditedAdminMockData:
application = None application = None
match domain_type: match domain_type:
case self.APPLICATION: case self.APPLICATION:
application = self.create_full_dummy_domain_application( application = self.create_full_dummy_domain_application(item_name, status)
item_name, status
)
case self.INVITATION: case self.INVITATION:
application = self.create_full_dummy_domain_invitation( application = self.create_full_dummy_domain_invitation(item_name, status)
item_name, status
)
case self.INFORMATION: case self.INFORMATION:
application = self.create_full_dummy_domain_information( application = self.create_full_dummy_domain_information(item_name, status)
item_name, status
)
case _: case _:
raise ValueError("Invalid domain_type, must conform to given constants") raise ValueError("Invalid domain_type, must conform to given constants")
@ -527,9 +507,7 @@ def completed_application(
if has_anything_else: if has_anything_else:
domain_application_kwargs["anything_else"] = "There is more" domain_application_kwargs["anything_else"] = "There is more"
application, _ = DomainApplication.objects.get_or_create( application, _ = DomainApplication.objects.get_or_create(**domain_application_kwargs)
**domain_application_kwargs
)
if has_other_contacts: if has_other_contacts:
application.other_contacts.add(other) application.other_contacts.add(other)
@ -631,11 +609,7 @@ class MockEppLib(TestCase):
mockDataInfoDomain = fakedEppObject( mockDataInfoDomain = fakedEppObject(
"fakePw", "fakePw",
cr_date=datetime.datetime(2023, 5, 25, 19, 45, 35), cr_date=datetime.datetime(2023, 5, 25, 19, 45, 35),
contacts=[ contacts=[common.DomainContact(contact="123", type=PublicContact.ContactTypeChoices.SECURITY)],
common.DomainContact(
contact="123", type=PublicContact.ContactTypeChoices.SECURITY
)
],
hosts=["fake.host.com"], hosts=["fake.host.com"],
statuses=[ statuses=[
common.Status(state="serverTransferProhibited", description="", lang="en"), common.Status(state="serverTransferProhibited", description="", lang="en"),
@ -705,21 +679,11 @@ class MockEppLib(TestCase):
mockDefaultTechnicalContact = InfoDomainWithContacts.dummyInfoContactResultData( mockDefaultTechnicalContact = InfoDomainWithContacts.dummyInfoContactResultData(
"defaultTech", "dotgov@cisa.dhs.gov" "defaultTech", "dotgov@cisa.dhs.gov"
) )
mockDefaultSecurityContact = InfoDomainWithContacts.dummyInfoContactResultData( mockDefaultSecurityContact = InfoDomainWithContacts.dummyInfoContactResultData("defaultSec", "dotgov@cisa.dhs.gov")
"defaultSec", "dotgov@cisa.dhs.gov" mockSecurityContact = InfoDomainWithContacts.dummyInfoContactResultData("securityContact", "security@mail.gov")
) mockTechnicalContact = InfoDomainWithContacts.dummyInfoContactResultData("technicalContact", "tech@mail.gov")
mockSecurityContact = InfoDomainWithContacts.dummyInfoContactResultData( mockAdministrativeContact = InfoDomainWithContacts.dummyInfoContactResultData("adminContact", "admin@mail.gov")
"securityContact", "security@mail.gov" mockRegistrantContact = InfoDomainWithContacts.dummyInfoContactResultData("regContact", "registrant@mail.gov")
)
mockTechnicalContact = InfoDomainWithContacts.dummyInfoContactResultData(
"technicalContact", "tech@mail.gov"
)
mockAdministrativeContact = InfoDomainWithContacts.dummyInfoContactResultData(
"adminContact", "admin@mail.gov"
)
mockRegistrantContact = InfoDomainWithContacts.dummyInfoContactResultData(
"regContact", "registrant@mail.gov"
)
infoDomainNoContact = fakedEppObject( infoDomainNoContact = fakedEppObject(
"security", "security",
@ -759,9 +723,7 @@ class MockEppLib(TestCase):
addrs=[common.Ip(addr="1.2.3.4"), common.Ip(addr="2.3.4.5")], addrs=[common.Ip(addr="1.2.3.4"), common.Ip(addr="2.3.4.5")],
) )
mockDataHostChange = fakedEppObject( mockDataHostChange = fakedEppObject("lastPw", cr_date=datetime.datetime(2023, 8, 25, 19, 45, 35))
"lastPw", cr_date=datetime.datetime(2023, 8, 25, 19, 45, 35)
)
addDsData1 = { addDsData1 = {
"keyTag": 1234, "keyTag": 1234,
"alg": 3, "alg": 3,
@ -852,9 +814,7 @@ class MockEppLib(TestCase):
def _mockDomainName(self, _name, _avail=False): def _mockDomainName(self, _name, _avail=False):
return MagicMock( return MagicMock(
res_data=[ res_data=[
responses.check.CheckDomainResultData( responses.check.CheckDomainResultData(name=_name, avail=_avail, reason=None),
name=_name, avail=_avail, reason=None
),
] ]
) )
@ -927,9 +887,7 @@ class MockEppLib(TestCase):
name = getattr(_request, "name", None) name = getattr(_request, "name", None)
fake_nameserver = "ns1.failDelete.gov" fake_nameserver = "ns1.failDelete.gov"
if name in fake_nameserver: if name in fake_nameserver:
raise RegistryError( raise RegistryError(code=ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION)
code=ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION
)
return None return None
def mockInfoDomainCommands(self, _request, cleaned): def mockInfoDomainCommands(self, _request, cleaned):
@ -948,9 +906,7 @@ class MockEppLib(TestCase):
), ),
"dnssec-none.gov": (self.mockDataInfoDomain, None), "dnssec-none.gov": (self.mockDataInfoDomain, None),
"my-nameserver.gov": ( "my-nameserver.gov": (
self.infoDomainTwoHosts self.infoDomainTwoHosts if self.mockedSendFunction.call_count == 5 else self.infoDomainNoHost,
if self.mockedSendFunction.call_count == 5
else self.infoDomainNoHost,
None, None,
), ),
"nameserverwithip.gov": (self.infoDomainHasIP, None), "nameserverwithip.gov": (self.infoDomainHasIP, None),
@ -963,9 +919,7 @@ class MockEppLib(TestCase):
} }
# Retrieve the corresponding values from the dictionary # Retrieve the corresponding values from the dictionary
res_data, extensions = request_mappings.get( res_data, extensions = request_mappings.get(request_name, (self.mockDataInfoDomain, None))
request_name, (self.mockDataInfoDomain, None)
)
return MagicMock( return MagicMock(
res_data=[res_data], res_data=[res_data],
@ -996,10 +950,7 @@ class MockEppLib(TestCase):
return MagicMock(res_data=[mocked_result]) return MagicMock(res_data=[mocked_result])
def mockCreateContactCommands(self, _request, cleaned): def mockCreateContactCommands(self, _request, cleaned):
if ( if getattr(_request, "id", None) == "fail" and self.mockedSendFunction.call_count == 3:
getattr(_request, "id", None) == "fail"
and self.mockedSendFunction.call_count == 3
):
# use this for when a contact is being updated # use this for when a contact is being updated
# sets the second send() to fail # sets the second send() to fail
raise RegistryError(code=ErrorCode.OBJECT_EXISTS) raise RegistryError(code=ErrorCode.OBJECT_EXISTS)
@ -1019,9 +970,7 @@ class MockEppLib(TestCase):
self.mockedSendFunction = self.mockSendPatch.start() self.mockedSendFunction = self.mockSendPatch.start()
self.mockedSendFunction.side_effect = self.mockSend self.mockedSendFunction.side_effect = self.mockSend
def _convertPublicContactToEpp( def _convertPublicContactToEpp(self, contact: PublicContact, disclose_email=False, createContact=True):
self, contact: PublicContact, disclose_email=False, createContact=True
):
DF = common.DiscloseField DF = common.DiscloseField
fields = {DF.EMAIL} fields = {DF.EMAIL}
@ -1033,9 +982,7 @@ class MockEppLib(TestCase):
# check docs here looks like we may have more than one address field but # check docs here looks like we may have more than one address field but
addr = common.ContactAddr( addr = common.ContactAddr(
[ [
getattr(contact, street) getattr(contact, street) for street in ["street1", "street2", "street3"] if hasattr(contact, street)
for street in ["street1", "street2", "street3"]
if hasattr(contact, street)
], # type: ignore ], # type: ignore
city=contact.city, city=contact.city,
pc=contact.pc, pc=contact.pc,

View file

@ -67,9 +67,7 @@ class TestDomainAdmin(MockEppLib):
# for our actual application # for our actual application
self.assertContains(response, "Federal", count=4) self.assertContains(response, "Federal", count=4)
# This may be a bit more robust # This may be a bit more robust
self.assertContains( self.assertContains(response, '<td class="field-organization_type">Federal</td>', count=1)
response, '<td class="field-organization_type">Federal</td>', count=1
)
# Now let's make sure the long description does not exist # Now let's make sure the long description does not exist
self.assertNotContains(response, "Federal: an agency of the U.S. government") self.assertNotContains(response, "Federal: an agency of the U.S. government")
@ -130,12 +128,12 @@ class TestDomainAdmin(MockEppLib):
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertContains(response, domain.name) self.assertContains(response, domain.name)
self.assertContains(response, "Delete domain in registry") self.assertContains(response, "Remove from registry")
# Test the info dialog # Test the info dialog
request = self.factory.post( request = self.factory.post(
"/admin/registrar/domain/{}/change/".format(domain.pk), "/admin/registrar/domain/{}/change/".format(domain.pk),
{"_delete_domain": "Delete domain in registry", "name": domain.name}, {"_delete_domain": "Remove from registry", "name": domain.name},
follow=True, follow=True,
) )
request.user = self.client request.user = self.client
@ -170,12 +168,12 @@ class TestDomainAdmin(MockEppLib):
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertContains(response, domain.name) self.assertContains(response, domain.name)
self.assertContains(response, "Delete domain in registry") self.assertContains(response, "Remove from registry")
# Test the error # Test the error
request = self.factory.post( request = self.factory.post(
"/admin/registrar/domain/{}/change/".format(domain.pk), "/admin/registrar/domain/{}/change/".format(domain.pk),
{"_delete_domain": "Delete domain in registry", "name": domain.name}, {"_delete_domain": "Remove from registry", "name": domain.name},
follow=True, follow=True,
) )
request.user = self.client request.user = self.client
@ -215,12 +213,12 @@ class TestDomainAdmin(MockEppLib):
) )
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertContains(response, domain.name) self.assertContains(response, domain.name)
self.assertContains(response, "Delete domain in registry") self.assertContains(response, "Remove from registry")
# Test the info dialog # Test the info dialog
request = self.factory.post( request = self.factory.post(
"/admin/registrar/domain/{}/change/".format(domain.pk), "/admin/registrar/domain/{}/change/".format(domain.pk),
{"_delete_domain": "Delete domain in registry", "name": domain.name}, {"_delete_domain": "Remove from registry", "name": domain.name},
follow=True, follow=True,
) )
request.user = self.client request.user = self.client
@ -242,7 +240,7 @@ class TestDomainAdmin(MockEppLib):
# Test the info dialog # Test the info dialog
request = self.factory.post( request = self.factory.post(
"/admin/registrar/domain/{}/change/".format(domain.pk), "/admin/registrar/domain/{}/change/".format(domain.pk),
{"_delete_domain": "Delete domain in registry", "name": domain.name}, {"_delete_domain": "Remove from registry", "name": domain.name},
follow=True, follow=True,
) )
request.user = self.client request.user = self.client
@ -318,9 +316,7 @@ class TestDomainApplicationAdmin(MockEppLib):
super().setUp() super().setUp()
self.site = AdminSite() self.site = AdminSite()
self.factory = RequestFactory() self.factory = RequestFactory()
self.admin = DomainApplicationAdmin( self.admin = DomainApplicationAdmin(model=DomainApplication, admin_site=self.site)
model=DomainApplication, admin_site=self.site
)
self.superuser = create_superuser() self.superuser = create_superuser()
self.staffuser = create_user() self.staffuser = create_user()
@ -335,9 +331,7 @@ class TestDomainApplicationAdmin(MockEppLib):
# for our actual application # for our actual application
self.assertContains(response, "Federal", count=4) self.assertContains(response, "Federal", count=4)
# This may be a bit more robust # This may be a bit more robust
self.assertContains( self.assertContains(response, '<td class="field-organization_type">Federal</td>', count=1)
response, '<td class="field-organization_type">Federal</td>', count=1
)
# Now let's make sure the long description does not exist # Now let's make sure the long description does not exist
self.assertNotContains(response, "Federal: an agency of the U.S. government") self.assertNotContains(response, "Federal: an agency of the U.S. government")
@ -355,9 +349,7 @@ class TestDomainApplicationAdmin(MockEppLib):
application = completed_application() application = completed_application()
# Create a mock request # Create a mock request
request = self.factory.post( request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
"/admin/registrar/domainapplication/{}/change/".format(application.pk)
)
# Modify the application's property # Modify the application's property
application.status = DomainApplication.SUBMITTED application.status = DomainApplication.SUBMITTED
@ -398,9 +390,7 @@ class TestDomainApplicationAdmin(MockEppLib):
application = completed_application(status=DomainApplication.SUBMITTED) application = completed_application(status=DomainApplication.SUBMITTED)
# Create a mock request # Create a mock request
request = self.factory.post( request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
"/admin/registrar/domainapplication/{}/change/".format(application.pk)
)
# Modify the application's property # Modify the application's property
application.status = DomainApplication.IN_REVIEW application.status = DomainApplication.IN_REVIEW
@ -441,9 +431,7 @@ class TestDomainApplicationAdmin(MockEppLib):
application = completed_application(status=DomainApplication.IN_REVIEW) application = completed_application(status=DomainApplication.IN_REVIEW)
# Create a mock request # Create a mock request
request = self.factory.post( request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
"/admin/registrar/domainapplication/{}/change/".format(application.pk)
)
# Modify the application's property # Modify the application's property
application.status = DomainApplication.APPROVED application.status = DomainApplication.APPROVED
@ -479,9 +467,7 @@ class TestDomainApplicationAdmin(MockEppLib):
application = completed_application(status=DomainApplication.IN_REVIEW) application = completed_application(status=DomainApplication.IN_REVIEW)
# Create a mock request # Create a mock request
request = self.factory.post( request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
"/admin/registrar/domainapplication/{}/change/".format(application.pk)
)
# Modify the application's property # Modify the application's property
application.status = DomainApplication.APPROVED application.status = DomainApplication.APPROVED
@ -490,9 +476,7 @@ class TestDomainApplicationAdmin(MockEppLib):
self.admin.save_model(request, application, form=None, change=True) self.admin.save_model(request, application, form=None, change=True)
# Test that approved domain exists and equals requested domain # Test that approved domain exists and equals requested domain
self.assertEqual( self.assertEqual(application.requested_domain.name, application.approved_domain.name)
application.requested_domain.name, application.approved_domain.name
)
@boto3_mocking.patching @boto3_mocking.patching
def test_save_model_sends_action_needed_email(self): def test_save_model_sends_action_needed_email(self):
@ -508,9 +492,7 @@ class TestDomainApplicationAdmin(MockEppLib):
application = completed_application(status=DomainApplication.IN_REVIEW) application = completed_application(status=DomainApplication.IN_REVIEW)
# Create a mock request # Create a mock request
request = self.factory.post( request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
"/admin/registrar/domainapplication/{}/change/".format(application.pk)
)
# Modify the application's property # Modify the application's property
application.status = DomainApplication.ACTION_NEEDED application.status = DomainApplication.ACTION_NEEDED
@ -529,10 +511,7 @@ class TestDomainApplicationAdmin(MockEppLib):
email_body = email_content["Simple"]["Body"]["Text"]["Data"] email_body = email_content["Simple"]["Body"]["Text"]["Data"]
# Assert or perform other checks on the email details # Assert or perform other checks on the email details
expected_string = ( expected_string = "We've identified an action needed to complete the review of your .gov domain request."
"We've identified an action needed to complete the "
"review of your .gov domain request."
)
self.assertEqual(from_email, settings.DEFAULT_FROM_EMAIL) self.assertEqual(from_email, settings.DEFAULT_FROM_EMAIL)
self.assertEqual(to_email, EMAIL) self.assertEqual(to_email, EMAIL)
self.assertIn(expected_string, email_body) self.assertIn(expected_string, email_body)
@ -554,9 +533,7 @@ class TestDomainApplicationAdmin(MockEppLib):
application = completed_application(status=DomainApplication.IN_REVIEW) application = completed_application(status=DomainApplication.IN_REVIEW)
# Create a mock request # Create a mock request
request = self.factory.post( request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
"/admin/registrar/domainapplication/{}/change/".format(application.pk)
)
# Modify the application's property # Modify the application's property
application.status = DomainApplication.REJECTED application.status = DomainApplication.REJECTED
@ -592,9 +569,7 @@ class TestDomainApplicationAdmin(MockEppLib):
application = completed_application(status=DomainApplication.IN_REVIEW) application = completed_application(status=DomainApplication.IN_REVIEW)
# Create a mock request # Create a mock request
request = self.factory.post( request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
"/admin/registrar/domainapplication/{}/change/".format(application.pk)
)
# Modify the application's property # Modify the application's property
application.status = DomainApplication.INELIGIBLE application.status = DomainApplication.INELIGIBLE
@ -699,8 +674,7 @@ class TestDomainApplicationAdmin(MockEppLib):
# Assert that the error message was called with the correct argument # Assert that the error message was called with the correct argument
mock_error.assert_called_once_with( mock_error.assert_called_once_with(
request, request,
"This action is not permitted for applications " "This action is not permitted for applications with a restricted creator.",
+ "with a restricted creator.",
) )
# Assert that the status has not changed # Assert that the status has not changed
@ -714,9 +688,7 @@ class TestDomainApplicationAdmin(MockEppLib):
with patch("django.contrib.messages.warning") as mock_warning: with patch("django.contrib.messages.warning") as mock_warning:
# Create a request object with a superuser # Create a request object with a superuser
request = self.factory.get( request = self.factory.get("/admin/your_app/domainapplication/{}/change/".format(application.pk))
"/admin/your_app/domainapplication/{}/change/".format(application.pk)
)
request.user = self.superuser request.user = self.superuser
self.admin.display_restricted_warning(request, application) self.admin.display_restricted_warning(request, application)
@ -735,9 +707,7 @@ class TestDomainApplicationAdmin(MockEppLib):
application.save() application.save()
# Create a request object with a superuser # Create a request object with a superuser
request = self.factory.post( request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
"/admin/registrar/domainapplication/{}/change/".format(application.pk)
)
request.user = self.superuser request.user = self.superuser
# Define a custom implementation for is_active # Define a custom implementation for is_active
@ -764,16 +734,12 @@ class TestDomainApplicationAdmin(MockEppLib):
# Create an instance of the model # Create an instance of the model
application = completed_application(status=DomainApplication.APPROVED) application = completed_application(status=DomainApplication.APPROVED)
domain = Domain.objects.create(name=application.requested_domain.name) domain = Domain.objects.create(name=application.requested_domain.name)
domain_information = DomainInformation.objects.create( domain_information = DomainInformation.objects.create(creator=self.superuser, domain=domain)
creator=self.superuser, domain=domain
)
application.approved_domain = domain application.approved_domain = domain
application.save() application.save()
# Create a request object with a superuser # Create a request object with a superuser
request = self.factory.post( request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
"/admin/registrar/domainapplication/{}/change/".format(application.pk)
)
request.user = self.superuser request.user = self.superuser
# Define a custom implementation for is_active # Define a custom implementation for is_active
@ -811,9 +777,7 @@ class TestDomainApplicationAdmin(MockEppLib):
application.save() application.save()
# Create a request object with a superuser # Create a request object with a superuser
request = self.factory.post( request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
"/admin/registrar/domainapplication/{}/change/".format(application.pk)
)
request.user = self.superuser request.user = self.superuser
# Define a custom implementation for is_active # Define a custom implementation for is_active
@ -840,16 +804,12 @@ class TestDomainApplicationAdmin(MockEppLib):
# Create an instance of the model # Create an instance of the model
application = completed_application(status=DomainApplication.APPROVED) application = completed_application(status=DomainApplication.APPROVED)
domain = Domain.objects.create(name=application.requested_domain.name) domain = Domain.objects.create(name=application.requested_domain.name)
domain_information = DomainInformation.objects.create( domain_information = DomainInformation.objects.create(creator=self.superuser, domain=domain)
creator=self.superuser, domain=domain
)
application.approved_domain = domain application.approved_domain = domain
application.save() application.save()
# Create a request object with a superuser # Create a request object with a superuser
request = self.factory.post( request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
"/admin/registrar/domainapplication/{}/change/".format(application.pk)
)
request.user = self.superuser request.user = self.superuser
# Define a custom implementation for is_active # Define a custom implementation for is_active
@ -1012,17 +972,13 @@ class AuditedAdminTest(TestCase):
self.factory = RequestFactory() self.factory = RequestFactory()
self.client = Client(HTTP_HOST="localhost:8080") self.client = Client(HTTP_HOST="localhost:8080")
def order_by_desired_field_helper( def order_by_desired_field_helper(self, obj_to_sort: AuditedAdmin, request, field_name, *obj_names):
self, obj_to_sort: AuditedAdmin, request, field_name, *obj_names
):
formatted_sort_fields = [] formatted_sort_fields = []
for obj in obj_names: for obj in obj_names:
formatted_sort_fields.append("{}__{}".format(field_name, obj)) formatted_sort_fields.append("{}__{}".format(field_name, obj))
ordered_list = list( ordered_list = list(
obj_to_sort.get_queryset(request) obj_to_sort.get_queryset(request).order_by(*formatted_sort_fields).values_list(*formatted_sort_fields)
.order_by(*formatted_sort_fields)
.values_list(*formatted_sort_fields)
) )
return ordered_list return ordered_list
@ -1040,9 +996,7 @@ class AuditedAdminTest(TestCase):
applications = multiple_unalphabetical_domain_objects("application") applications = multiple_unalphabetical_domain_objects("application")
# Create a mock request # Create a mock request
request = self.factory.post( request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(applications[0].pk))
"/admin/registrar/domainapplication/{}/change/".format(applications[0].pk)
)
model_admin = AuditedAdmin(DomainApplication, self.site) model_admin = AuditedAdmin(DomainApplication, self.site)
@ -1058,12 +1012,8 @@ class AuditedAdminTest(TestCase):
sorted_fields = ["first_name", "last_name"] sorted_fields = ["first_name", "last_name"]
# We want both of these to be lists, as it is richer test wise. # We want both of these to be lists, as it is richer test wise.
desired_order = self.order_by_desired_field_helper( desired_order = self.order_by_desired_field_helper(model_admin, request, field.name, *sorted_fields)
model_admin, request, field.name, *sorted_fields current_sort_order = list(model_admin.formfield_for_foreignkey(field, request).queryset)
)
current_sort_order = list(
model_admin.formfield_for_foreignkey(field, request).queryset
)
# Conforms to the same object structure as desired_order # Conforms to the same object structure as desired_order
current_sort_order_coerced_type = [] current_sort_order_coerced_type = []
@ -1101,9 +1051,7 @@ class AuditedAdminTest(TestCase):
applications = multiple_unalphabetical_domain_objects("information") applications = multiple_unalphabetical_domain_objects("information")
# Create a mock request # Create a mock request
request = self.factory.post( request = self.factory.post("/admin/registrar/domaininformation/{}/change/".format(applications[0].pk))
"/admin/registrar/domaininformation/{}/change/".format(applications[0].pk)
)
model_admin = AuditedAdmin(DomainInformation, self.site) model_admin = AuditedAdmin(DomainInformation, self.site)
@ -1121,12 +1069,8 @@ class AuditedAdminTest(TestCase):
sorted_fields = ["first_name", "last_name"] sorted_fields = ["first_name", "last_name"]
field_obj = field field_obj = field
# We want both of these to be lists, as it is richer test wise. # We want both of these to be lists, as it is richer test wise.
desired_order = self.order_by_desired_field_helper( desired_order = self.order_by_desired_field_helper(model_admin, request, field_obj.name, *sorted_fields)
model_admin, request, field_obj.name, *sorted_fields current_sort_order = list(model_admin.formfield_for_foreignkey(field_obj, request).queryset)
)
current_sort_order = list(
model_admin.formfield_for_foreignkey(field_obj, request).queryset
)
# Conforms to the same object structure as desired_order # Conforms to the same object structure as desired_order
current_sort_order_coerced_type = [] current_sort_order_coerced_type = []
@ -1144,9 +1088,7 @@ class AuditedAdminTest(TestCase):
elif field_obj == DomainInformation.domain_application.field: elif field_obj == DomainInformation.domain_application.field:
first = obj.requested_domain.name first = obj.requested_domain.name
name_tuple = self.coerced_fk_field_helper( name_tuple = self.coerced_fk_field_helper(first, last, field_obj.name, ":")
first, last, field_obj.name, ":"
)
if name_tuple is not None: if name_tuple is not None:
current_sort_order_coerced_type.append(name_tuple) current_sort_order_coerced_type.append(name_tuple)
@ -1163,9 +1105,7 @@ class AuditedAdminTest(TestCase):
applications = multiple_unalphabetical_domain_objects("invitation") applications = multiple_unalphabetical_domain_objects("invitation")
# Create a mock request # Create a mock request
request = self.factory.post( request = self.factory.post("/admin/registrar/domaininvitation/{}/change/".format(applications[0].pk))
"/admin/registrar/domaininvitation/{}/change/".format(applications[0].pk)
)
model_admin = AuditedAdmin(DomainInvitation, self.site) model_admin = AuditedAdmin(DomainInvitation, self.site)
@ -1177,12 +1117,8 @@ class AuditedAdminTest(TestCase):
sorted_fields = ["name"] sorted_fields = ["name"]
# We want both of these to be lists, as it is richer test wise. # We want both of these to be lists, as it is richer test wise.
desired_order = self.order_by_desired_field_helper( desired_order = self.order_by_desired_field_helper(model_admin, request, field.name, *sorted_fields)
model_admin, request, field.name, *sorted_fields current_sort_order = list(model_admin.formfield_for_foreignkey(field, request).queryset)
)
current_sort_order = list(
model_admin.formfield_for_foreignkey(field, request).queryset
)
# Conforms to the same object structure as desired_order # Conforms to the same object structure as desired_order
current_sort_order_coerced_type = [] current_sort_order_coerced_type = []
@ -1204,9 +1140,7 @@ class AuditedAdminTest(TestCase):
"{} is not ordered alphabetically".format(field.name), "{} is not ordered alphabetically".format(field.name),
) )
def coerced_fk_field_helper( def coerced_fk_field_helper(self, first_name, last_name, field_name, queryset_shorthand):
self, first_name, last_name, field_name, queryset_shorthand
):
"""Handles edge cases for test cases""" """Handles edge cases for test cases"""
if first_name is None: if first_name is None:
raise ValueError("Invalid value for first_name, must be defined") raise ValueError("Invalid value for first_name, must be defined")
@ -1256,9 +1190,7 @@ class DomainSessionVariableTest(TestCase):
p = "adminpass" p = "adminpass"
self.client.login(username="superuser", password=p) self.client.login(username="superuser", password=p)
dummy_domain_information: Domain = generic_domain_object( dummy_domain_information: Domain = generic_domain_object("information", "session")
"information", "session"
)
dummy_domain_information.domain.pk = 1 dummy_domain_information.domain.pk = 1
request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk) request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk)
@ -1275,9 +1207,7 @@ class DomainSessionVariableTest(TestCase):
dummy_domain_information = generic_domain_object("information", "session") dummy_domain_information = generic_domain_object("information", "session")
request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk) request = self.get_factory_post_edit_domain(dummy_domain_information.domain.pk)
self.populate_session_values( self.populate_session_values(request, dummy_domain_information.domain, preload_bad_data=True)
request, dummy_domain_information.domain, preload_bad_data=True
)
self.assertEqual(request.session["analyst_action"], "edit") self.assertEqual(request.session["analyst_action"], "edit")
self.assertEqual( self.assertEqual(
@ -1291,9 +1221,7 @@ class DomainSessionVariableTest(TestCase):
p = "adminpass" p = "adminpass"
self.client.login(username="superuser", password=p) self.client.login(username="superuser", password=p)
dummy_domain_information_list = multiple_unalphabetical_domain_objects( dummy_domain_information_list = multiple_unalphabetical_domain_objects("information")
"information"
)
for item in dummy_domain_information_list: for item in dummy_domain_information_list:
request = self.get_factory_post_edit_domain(item.domain.pk) request = self.get_factory_post_edit_domain(item.domain.pk)
self.populate_session_values(request, item.domain) self.populate_session_values(request, item.domain)

View file

@ -42,10 +42,7 @@ class TestFormValidation(MockEppLib):
form = CurrentSitesForm(data={"website": "nah"}) form = CurrentSitesForm(data={"website": "nah"})
self.assertEqual( self.assertEqual(
form.errors["website"], form.errors["website"],
[ ["Enter your organization's current website in the required format, like www.city.com."],
"Enter your organization's current website in the required format, like"
" www.city.com."
],
) )
def test_website_valid(self): def test_website_valid(self):
@ -82,10 +79,7 @@ class TestFormValidation(MockEppLib):
form = DotGovDomainForm(data={"requested_domain": "underscores_forever"}) form = DotGovDomainForm(data={"requested_domain": "underscores_forever"})
self.assertEqual( self.assertEqual(
form.errors["requested_domain"], form.errors["requested_domain"],
[ ["Enter a domain using only letters, numbers, or hyphens (though we don't recommend using hyphens)."],
"Enter a domain using only letters, numbers, or hyphens (though we"
" don't recommend using hyphens)."
],
) )
def test_authorizing_official_email_invalid(self): def test_authorizing_official_email_invalid(self):
@ -187,9 +181,7 @@ class TestFormValidation(MockEppLib):
def test_authorizing_official_phone_invalid(self): def test_authorizing_official_phone_invalid(self):
"""Must be a valid phone number.""" """Must be a valid phone number."""
form = AuthorizingOfficialForm(data={"phone": "boss@boss"}) form = AuthorizingOfficialForm(data={"phone": "boss@boss"})
self.assertTrue( self.assertTrue(form.errors["phone"][0].startswith("Enter a valid phone number "))
form.errors["phone"][0].startswith("Enter a valid phone number ")
)
def test_your_contact_email_invalid(self): def test_your_contact_email_invalid(self):
"""must be a valid email address.""" """must be a valid email address."""
@ -202,9 +194,7 @@ class TestFormValidation(MockEppLib):
def test_your_contact_phone_invalid(self): def test_your_contact_phone_invalid(self):
"""Must be a valid phone number.""" """Must be a valid phone number."""
form = YourContactForm(data={"phone": "boss@boss"}) form = YourContactForm(data={"phone": "boss@boss"})
self.assertTrue( self.assertTrue(form.errors["phone"][0].startswith("Enter a valid phone number "))
form.errors["phone"][0].startswith("Enter a valid phone number ")
)
def test_other_contact_email_invalid(self): def test_other_contact_email_invalid(self):
"""must be a valid email address.""" """must be a valid email address."""
@ -217,19 +207,14 @@ class TestFormValidation(MockEppLib):
def test_other_contact_phone_invalid(self): def test_other_contact_phone_invalid(self):
"""Must be a valid phone number.""" """Must be a valid phone number."""
form = OtherContactsForm(data={"phone": "boss@boss"}) form = OtherContactsForm(data={"phone": "boss@boss"})
self.assertTrue( self.assertTrue(form.errors["phone"][0].startswith("Enter a valid phone number "))
form.errors["phone"][0].startswith("Enter a valid phone number ")
)
def test_requirements_form_blank(self): def test_requirements_form_blank(self):
"""Requirements box unchecked is an error.""" """Requirements box unchecked is an error."""
form = RequirementsForm(data={}) form = RequirementsForm(data={})
self.assertEqual( self.assertEqual(
form.errors["is_policy_acknowledged"], form.errors["is_policy_acknowledged"],
[ ["Check the box if you read and agree to the requirements for operating .gov domains."],
"Check the box if you read and agree to the requirements for"
" operating .gov domains."
],
) )
def test_requirements_form_unchecked(self): def test_requirements_form_unchecked(self):
@ -237,23 +222,13 @@ class TestFormValidation(MockEppLib):
form = RequirementsForm(data={"is_policy_acknowledged": False}) form = RequirementsForm(data={"is_policy_acknowledged": False})
self.assertEqual( self.assertEqual(
form.errors["is_policy_acknowledged"], form.errors["is_policy_acknowledged"],
[ ["Check the box if you read and agree to the requirements for operating .gov domains."],
"Check the box if you read and agree to the requirements for"
" operating .gov domains."
],
) )
def test_tribal_government_unrecognized(self): def test_tribal_government_unrecognized(self):
"""Not state or federally recognized is an error.""" """Not state or federally recognized is an error."""
form = TribalGovernmentForm( form = TribalGovernmentForm(data={"state_recognized": False, "federally_recognized": False})
data={"state_recognized": False, "federally_recognized": False} self.assertTrue(any("tell us more about your tribe" in error for error in form.non_field_errors()))
)
self.assertTrue(
any(
"tell us more about your tribe" in error
for error in form.non_field_errors()
)
)
class TestContactForm(TestCase): class TestContactForm(TestCase):

View file

@ -22,14 +22,10 @@ class TestGroups(TestCase):
full_access_group = UserGroup.objects.get(name="full_access_group") full_access_group = UserGroup.objects.get(name="full_access_group")
# Assert that the cisa_analysts_group exists in the database # Assert that the cisa_analysts_group exists in the database
self.assertQuerysetEqual( self.assertQuerysetEqual(UserGroup.objects.filter(name="cisa_analysts_group"), [cisa_analysts_group])
UserGroup.objects.filter(name="cisa_analysts_group"), [cisa_analysts_group]
)
# Assert that the full_access_group exists in the database # Assert that the full_access_group exists in the database
self.assertQuerysetEqual( self.assertQuerysetEqual(UserGroup.objects.filter(name="full_access_group"), [full_access_group])
UserGroup.objects.filter(name="full_access_group"), [full_access_group]
)
# Test permissions for cisa_analysts_group # Test permissions for cisa_analysts_group
# Verifies permission data migrations ran as expected. # Verifies permission data migrations ran as expected.

View file

@ -104,9 +104,7 @@ class TestDomainApplication(TestCase):
def test_status_fsm_submit_succeed(self): def test_status_fsm_submit_succeed(self):
user, _ = User.objects.get_or_create() user, _ = User.objects.get_or_create()
site = DraftDomain.objects.create(name="igorville.gov") site = DraftDomain.objects.create(name="igorville.gov")
application = DomainApplication.objects.create( application = DomainApplication.objects.create(creator=user, requested_domain=site)
creator=user, requested_domain=site
)
# no submitter email so this emits a log warning # no submitter email so this emits a log warning
with less_console_noise(): with less_console_noise():
application.submit() application.submit()
@ -543,9 +541,7 @@ class TestPermissions(TestCase):
def test_approval_creates_role(self): def test_approval_creates_role(self):
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
user, _ = User.objects.get_or_create() user, _ = User.objects.get_or_create()
application = DomainApplication.objects.create( application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain)
creator=user, requested_domain=draft_domain
)
# skip using the submit method # skip using the submit method
application.status = DomainApplication.SUBMITTED application.status = DomainApplication.SUBMITTED
application.approve() application.approve()
@ -562,9 +558,7 @@ class TestDomainInfo(TestCase):
def test_approval_creates_info(self): def test_approval_creates_info(self):
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
user, _ = User.objects.get_or_create() user, _ = User.objects.get_or_create()
application = DomainApplication.objects.create( application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain)
creator=user, requested_domain=draft_domain
)
# skip using the submit method # skip using the submit method
application.status = DomainApplication.SUBMITTED application.status = DomainApplication.SUBMITTED
application.approve() application.approve()
@ -581,9 +575,7 @@ class TestInvitations(TestCase):
def setUp(self): def setUp(self):
self.domain, _ = Domain.objects.get_or_create(name="igorville.gov") self.domain, _ = Domain.objects.get_or_create(name="igorville.gov")
self.email = "mayor@igorville.gov" self.email = "mayor@igorville.gov"
self.invitation, _ = DomainInvitation.objects.get_or_create( self.invitation, _ = DomainInvitation.objects.get_or_create(email=self.email, domain=self.domain)
email=self.email, domain=self.domain
)
self.user, _ = User.objects.get_or_create(email=self.email) self.user, _ = User.objects.get_or_create(email=self.email)
# clean out the roles each time # clean out the roles each time
@ -601,17 +593,15 @@ class TestInvitations(TestCase):
def test_retrieve_existing_role_no_error(self): def test_retrieve_existing_role_no_error(self):
# make the overlapping role # make the overlapping role
UserDomainRole.objects.get_or_create( UserDomainRole.objects.get_or_create(user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER)
user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER
)
# this is not an error but does produce a console warning # this is not an error but does produce a console warning
with less_console_noise(): with less_console_noise():
self.invitation.retrieve() self.invitation.retrieve()
self.assertEqual(self.invitation.status, DomainInvitation.RETRIEVED) self.assertEqual(self.invitation.status, DomainInvitation.RETRIEVED)
def test_retrieve_on_first_login(self): def test_retrieve_on_each_login(self):
"""A new user's first_login callback retrieves their invitations.""" """A user's authenticate on_each_login callback retrieves their invitations."""
self.user.first_login() self.user.on_each_login()
self.assertTrue(UserDomainRole.objects.get(user=self.user, domain=self.domain)) self.assertTrue(UserDomainRole.objects.get(user=self.user, domain=self.domain))
@ -627,9 +617,7 @@ class TestUser(TestCase):
# clean out the roles each time # clean out the roles each time
UserDomainRole.objects.all().delete() UserDomainRole.objects.all().delete()
TransitionDomain.objects.get_or_create( TransitionDomain.objects.get_or_create(username="mayor@igorville.gov", domain_name=self.domain_name)
username="mayor@igorville.gov", domain_name=self.domain_name
)
def tearDown(self): def tearDown(self):
super().tearDown() super().tearDown()
@ -640,19 +628,19 @@ class TestUser(TestCase):
User.objects.all().delete() User.objects.all().delete()
def test_check_transition_domains_on_login(self): def test_check_transition_domains_on_login(self):
"""A new user's first_login callback checks transition domains. """A user's on_each_login callback checks transition domains.
Makes DomainInformation object.""" Makes DomainInformation object."""
self.domain, _ = Domain.objects.get_or_create(name=self.domain_name) self.domain, _ = Domain.objects.get_or_create(name=self.domain_name)
self.user.first_login() self.user.on_each_login()
self.assertTrue(DomainInformation.objects.get(domain=self.domain)) self.assertTrue(DomainInformation.objects.get(domain=self.domain))
def test_check_transition_domains_without_domains_on_login(self): def test_check_transition_domains_without_domains_on_login(self):
"""A new user's first_login callback checks transition domains. """A user's on_each_login callback checks transition domains.
This test makes sure that in the event a domain does not exist This test makes sure that in the event a domain does not exist
for a given transition domain, both a domain and domain invitation for a given transition domain, both a domain and domain invitation
are created.""" are created."""
self.user.first_login() self.user.on_each_login()
self.assertTrue(Domain.objects.get(name=self.domain_name)) self.assertTrue(Domain.objects.get(name=self.domain_name))
domain = Domain.objects.get(name=self.domain_name) domain = Domain.objects.get(name=self.domain_name)

View file

@ -84,9 +84,7 @@ class TestDomainCache(MockEppLib):
# send was only called once & not on the second getter call # send was only called once & not on the second getter call
expectedCalls = [ expectedCalls = [
call( call(commands.InfoDomain(name="igorville.gov", auth_info=None), cleaned=True),
commands.InfoDomain(name="igorville.gov", auth_info=None), cleaned=True
),
] ]
self.mockedSendFunction.assert_has_calls(expectedCalls) self.mockedSendFunction.assert_has_calls(expectedCalls)
@ -261,9 +259,7 @@ class TestDomainCreation(MockEppLib):
""" """
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov") draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville.gov")
user, _ = User.objects.get_or_create() user, _ = User.objects.get_or_create()
application = DomainApplication.objects.create( application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain)
creator=user, requested_domain=draft_domain
)
# skip using the submit method # skip using the submit method
application.status = DomainApplication.SUBMITTED application.status = DomainApplication.SUBMITTED
# transition to approve state # transition to approve state
@ -412,11 +408,7 @@ class TestDomainAvailable(MockEppLib):
def side_effect(_request, cleaned): def side_effect(_request, cleaned):
return MagicMock( return MagicMock(
res_data=[ res_data=[responses.check.CheckDomainResultData(name="available.gov", avail=True, reason=None)],
responses.check.CheckDomainResultData(
name="available.gov", avail=True, reason=None
)
],
) )
patcher = patch("registrar.models.domain.registry.send") patcher = patch("registrar.models.domain.registry.send")
@ -449,11 +441,7 @@ class TestDomainAvailable(MockEppLib):
def side_effect(_request, cleaned): def side_effect(_request, cleaned):
return MagicMock( return MagicMock(
res_data=[ res_data=[responses.check.CheckDomainResultData(name="unavailable.gov", avail=False, reason="In Use")],
responses.check.CheckDomainResultData(
name="unavailable.gov", avail=False, reason="In Use"
)
],
) )
patcher = patch("registrar.models.domain.registry.send") patcher = patch("registrar.models.domain.registry.send")
@ -556,16 +544,10 @@ class TestRegistrantContacts(MockEppLib):
).registry_id ).registry_id
expectedSecContact.registry_id = id expectedSecContact.registry_id = id
expectedCreateCommand = self._convertPublicContactToEpp( expectedCreateCommand = self._convertPublicContactToEpp(expectedSecContact, disclose_email=False)
expectedSecContact, disclose_email=False
)
expectedUpdateDomain = commands.UpdateDomain( expectedUpdateDomain = commands.UpdateDomain(
name=self.domain.name, name=self.domain.name,
add=[ add=[common.DomainContact(contact=expectedSecContact.registry_id, type="security")],
common.DomainContact(
contact=expectedSecContact.registry_id, type="security"
)
],
) )
self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True)
@ -594,17 +576,11 @@ class TestRegistrantContacts(MockEppLib):
expectedSecContact.save() expectedSecContact.save()
# no longer the default email it should be disclosed # no longer the default email it should be disclosed
expectedCreateCommand = self._convertPublicContactToEpp( expectedCreateCommand = self._convertPublicContactToEpp(expectedSecContact, disclose_email=True)
expectedSecContact, disclose_email=True
)
expectedUpdateDomain = commands.UpdateDomain( expectedUpdateDomain = commands.UpdateDomain(
name=self.domain.name, name=self.domain.name,
add=[ add=[common.DomainContact(contact=expectedSecContact.registry_id, type="security")],
common.DomainContact(
contact=expectedSecContact.registry_id, type="security"
)
],
) )
# check that send has triggered the create command for the contact # check that send has triggered the create command for the contact
@ -630,17 +606,11 @@ class TestRegistrantContacts(MockEppLib):
self.domain.security_contact = security_contact self.domain.security_contact = security_contact
expectedCreateCommand = self._convertPublicContactToEpp( expectedCreateCommand = self._convertPublicContactToEpp(security_contact, disclose_email=False)
security_contact, disclose_email=False
)
expectedUpdateDomain = commands.UpdateDomain( expectedUpdateDomain = commands.UpdateDomain(
name=self.domain.name, name=self.domain.name,
add=[ add=[common.DomainContact(contact=security_contact.registry_id, type="security")],
common.DomainContact(
contact=security_contact.registry_id, type="security"
)
],
) )
expected_calls = [ expected_calls = [
call(expectedCreateCommand, cleaned=True), call(expectedCreateCommand, cleaned=True),
@ -671,38 +641,26 @@ class TestRegistrantContacts(MockEppLib):
new_contact.email = "" new_contact.email = ""
self.domain.security_contact = new_contact self.domain.security_contact = new_contact
firstCreateContactCall = self._convertPublicContactToEpp( firstCreateContactCall = self._convertPublicContactToEpp(old_contact, disclose_email=True)
old_contact, disclose_email=True
)
updateDomainAddCall = commands.UpdateDomain( updateDomainAddCall = commands.UpdateDomain(
name=self.domain.name, name=self.domain.name,
add=[ add=[common.DomainContact(contact=old_contact.registry_id, type="security")],
common.DomainContact(contact=old_contact.registry_id, type="security")
],
) )
self.assertEqual( self.assertEqual(
PublicContact.objects.filter(domain=self.domain).get().email, PublicContact.objects.filter(domain=self.domain).get().email,
PublicContact.get_default_security().email, PublicContact.get_default_security().email,
) )
# this one triggers the fail # this one triggers the fail
secondCreateContact = self._convertPublicContactToEpp( secondCreateContact = self._convertPublicContactToEpp(new_contact, disclose_email=True)
new_contact, disclose_email=True
)
updateDomainRemCall = commands.UpdateDomain( updateDomainRemCall = commands.UpdateDomain(
name=self.domain.name, name=self.domain.name,
rem=[ rem=[common.DomainContact(contact=old_contact.registry_id, type="security")],
common.DomainContact(contact=old_contact.registry_id, type="security")
],
) )
defaultSecID = ( defaultSecID = PublicContact.objects.filter(domain=self.domain).get().registry_id
PublicContact.objects.filter(domain=self.domain).get().registry_id
)
default_security = PublicContact.get_default_security() default_security = PublicContact.get_default_security()
default_security.registry_id = defaultSecID default_security.registry_id = defaultSecID
createDefaultContact = self._convertPublicContactToEpp( createDefaultContact = self._convertPublicContactToEpp(default_security, disclose_email=False)
default_security, disclose_email=False
)
updateDomainWDefault = commands.UpdateDomain( updateDomainWDefault = commands.UpdateDomain(
name=self.domain.name, name=self.domain.name,
add=[common.DomainContact(contact=defaultSecID, type="security")], add=[common.DomainContact(contact=defaultSecID, type="security")],
@ -731,26 +689,16 @@ class TestRegistrantContacts(MockEppLib):
security_contact.email = "originalUserEmail@gmail.com" security_contact.email = "originalUserEmail@gmail.com"
security_contact.registry_id = "fail" security_contact.registry_id = "fail"
security_contact.save() security_contact.save()
expectedCreateCommand = self._convertPublicContactToEpp( expectedCreateCommand = self._convertPublicContactToEpp(security_contact, disclose_email=True)
security_contact, disclose_email=True
)
expectedUpdateDomain = commands.UpdateDomain( expectedUpdateDomain = commands.UpdateDomain(
name=self.domain.name, name=self.domain.name,
add=[ add=[common.DomainContact(contact=security_contact.registry_id, type="security")],
common.DomainContact(
contact=security_contact.registry_id, type="security"
)
],
) )
security_contact.email = "changedEmail@email.com" security_contact.email = "changedEmail@email.com"
security_contact.save() security_contact.save()
expectedSecondCreateCommand = self._convertPublicContactToEpp( expectedSecondCreateCommand = self._convertPublicContactToEpp(security_contact, disclose_email=True)
security_contact, disclose_email=True updateContact = self._convertPublicContactToEpp(security_contact, disclose_email=True, createContact=False)
)
updateContact = self._convertPublicContactToEpp(
security_contact, disclose_email=True, createContact=False
)
expected_calls = [ expected_calls = [
call(expectedCreateCommand, cleaned=True), call(expectedCreateCommand, cleaned=True),
@ -804,9 +752,7 @@ class TestRegistrantContacts(MockEppLib):
actual_contact = contact[1] actual_contact = contact[1]
is_security = expected_contact.contact_type == "security" is_security = expected_contact.contact_type == "security"
expectedCreateCommand = self._convertPublicContactToEpp( expectedCreateCommand = self._convertPublicContactToEpp(expected_contact, disclose_email=is_security)
expected_contact, disclose_email=is_security
)
# Should only be disclosed if the type is security, as the email is valid # Should only be disclosed if the type is security, as the email is valid
self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True)
@ -818,20 +764,14 @@ class TestRegistrantContacts(MockEppLib):
self.maxDiff = None self.maxDiff = None
domain, _ = Domain.objects.get_or_create(name="freeman.gov") domain, _ = Domain.objects.get_or_create(name="freeman.gov")
dummy_contact = domain.get_default_security_contact() dummy_contact = domain.get_default_security_contact()
test_disclose = self._convertPublicContactToEpp( test_disclose = self._convertPublicContactToEpp(dummy_contact, disclose_email=True).__dict__
dummy_contact, disclose_email=True test_not_disclose = self._convertPublicContactToEpp(dummy_contact, disclose_email=False).__dict__
).__dict__
test_not_disclose = self._convertPublicContactToEpp(
dummy_contact, disclose_email=False
).__dict__
# Separated for linter # Separated for linter
disclose_email_field = {common.DiscloseField.EMAIL} disclose_email_field = {common.DiscloseField.EMAIL}
expected_disclose = { expected_disclose = {
"auth_info": common.ContactAuthInfo(pw="2fooBAR123fooBaz"), "auth_info": common.ContactAuthInfo(pw="2fooBAR123fooBaz"),
"disclose": common.Disclose( "disclose": common.Disclose(flag=True, fields=disclose_email_field, types=None),
flag=True, fields=disclose_email_field, types=None
),
"email": "dotgov@cisa.dhs.gov", "email": "dotgov@cisa.dhs.gov",
"extensions": [], "extensions": [],
"fax": None, "fax": None,
@ -857,9 +797,7 @@ class TestRegistrantContacts(MockEppLib):
# Separated for linter # Separated for linter
expected_not_disclose = { expected_not_disclose = {
"auth_info": common.ContactAuthInfo(pw="2fooBAR123fooBaz"), "auth_info": common.ContactAuthInfo(pw="2fooBAR123fooBaz"),
"disclose": common.Disclose( "disclose": common.Disclose(flag=False, fields=disclose_email_field, types=None),
flag=False, fields=disclose_email_field, types=None
),
"email": "dotgov@cisa.dhs.gov", "email": "dotgov@cisa.dhs.gov",
"extensions": [], "extensions": [],
"fax": None, "fax": None,
@ -902,9 +840,7 @@ class TestRegistrantContacts(MockEppLib):
expectedSecContact.registry_id = "defaultSec" expectedSecContact.registry_id = "defaultSec"
domain.security_contact = expectedSecContact domain.security_contact = expectedSecContact
expectedCreateCommand = self._convertPublicContactToEpp( expectedCreateCommand = self._convertPublicContactToEpp(expectedSecContact, disclose_email=False)
expectedSecContact, disclose_email=False
)
self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True)
# Confirm that we are getting a default email # Confirm that we are getting a default email
@ -923,9 +859,7 @@ class TestRegistrantContacts(MockEppLib):
expectedTechContact.registry_id = "defaultTech" expectedTechContact.registry_id = "defaultTech"
domain.technical_contact = expectedTechContact domain.technical_contact = expectedTechContact
expectedCreateCommand = self._convertPublicContactToEpp( expectedCreateCommand = self._convertPublicContactToEpp(expectedTechContact, disclose_email=False)
expectedTechContact, disclose_email=False
)
self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True)
# Confirm that we are getting a default email # Confirm that we are getting a default email
@ -945,9 +879,7 @@ class TestRegistrantContacts(MockEppLib):
expectedSecContact.email = "123@mail.gov" expectedSecContact.email = "123@mail.gov"
domain.security_contact = expectedSecContact domain.security_contact = expectedSecContact
expectedCreateCommand = self._convertPublicContactToEpp( expectedCreateCommand = self._convertPublicContactToEpp(expectedSecContact, disclose_email=True)
expectedSecContact, disclose_email=True
)
self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True) self.mockedSendFunction.assert_any_call(expectedCreateCommand, cleaned=True)
# Confirm that we are getting the desired email # Confirm that we are getting the desired email
@ -972,9 +904,7 @@ class TestRegistrantContacts(MockEppLib):
) )
# Checks if we grabbed the correct PublicContact # Checks if we grabbed the correct PublicContact
self.assertEqual( self.assertEqual(self.domain_contact.security_contact.email, expected_contact.email)
self.domain_contact.security_contact.email, expected_contact.email
)
expected_contact_db = PublicContact.objects.filter( expected_contact_db = PublicContact.objects.filter(
registry_id=self.domain_contact.security_contact.registry_id, registry_id=self.domain_contact.security_contact.registry_id,
@ -1003,9 +933,7 @@ class TestRegistrantContacts(MockEppLib):
contact_type=technical, contact_type=technical,
) )
self.assertEqual( self.assertEqual(self.domain_contact.technical_contact.email, expected_contact.email)
self.domain_contact.technical_contact.email, expected_contact.email
)
# Checks if we grab the correct PublicContact # Checks if we grab the correct PublicContact
expected_contact_db = PublicContact.objects.filter( expected_contact_db = PublicContact.objects.filter(
@ -1035,9 +963,7 @@ class TestRegistrantContacts(MockEppLib):
contact_type=administrative, contact_type=administrative,
) )
self.assertEqual( self.assertEqual(self.domain_contact.administrative_contact.email, expected_contact.email)
self.domain_contact.administrative_contact.email, expected_contact.email
)
expected_contact_db = PublicContact.objects.filter( expected_contact_db = PublicContact.objects.filter(
registry_id=self.domain_contact.administrative_contact.registry_id, registry_id=self.domain_contact.administrative_contact.registry_id,
@ -1045,9 +971,7 @@ class TestRegistrantContacts(MockEppLib):
).get() ).get()
# Checks if we grab the correct PublicContact # Checks if we grab the correct PublicContact
self.assertEqual( self.assertEqual(self.domain_contact.administrative_contact, expected_contact_db)
self.domain_contact.administrative_contact, expected_contact_db
)
self.mockedSendFunction.assert_has_calls( self.mockedSendFunction.assert_has_calls(
[ [
call( call(
@ -1067,9 +991,7 @@ class TestRegistrantContacts(MockEppLib):
contact_type=PublicContact.ContactTypeChoices.REGISTRANT, contact_type=PublicContact.ContactTypeChoices.REGISTRANT,
) )
self.assertEqual( self.assertEqual(self.domain_contact.registrant_contact.email, expected_contact.email)
self.domain_contact.registrant_contact.email, expected_contact.email
)
expected_contact_db = PublicContact.objects.filter( expected_contact_db = PublicContact.objects.filter(
registry_id=self.domain_contact.registrant_contact.registry_id, registry_id=self.domain_contact.registrant_contact.registry_id,
@ -1104,9 +1026,7 @@ class TestRegistrantNameservers(MockEppLib):
self.nameserver2 = "ns1.my-nameserver-2.com" self.nameserver2 = "ns1.my-nameserver-2.com"
self.nameserver3 = "ns1.cats-are-superior3.com" self.nameserver3 = "ns1.cats-are-superior3.com"
self.domain, _ = Domain.objects.get_or_create( self.domain, _ = Domain.objects.get_or_create(name="my-nameserver.gov", state=Domain.State.DNS_NEEDED)
name="my-nameserver.gov", state=Domain.State.DNS_NEEDED
)
self.domainWithThreeNS, _ = Domain.objects.get_or_create( self.domainWithThreeNS, _ = Domain.objects.get_or_create(
name="threenameserversDomain.gov", state=Domain.State.READY name="threenameserversDomain.gov", state=Domain.State.READY
) )
@ -1476,9 +1396,7 @@ class TestRegistrantNameservers(MockEppLib):
with a different IP address(es) with a different IP address(es)
Then `commands.UpdateHost` is sent to the registry Then `commands.UpdateHost` is sent to the registry
""" """
domain, _ = Domain.objects.get_or_create( domain, _ = Domain.objects.get_or_create(name="nameserverwithip.gov", state=Domain.State.READY)
name="nameserverwithip.gov", state=Domain.State.READY
)
domain.nameservers = [ domain.nameservers = [
("ns1.nameserverwithip.gov", ["2.3.4.5", "1.2.3.4"]), ("ns1.nameserverwithip.gov", ["2.3.4.5", "1.2.3.4"]),
( (
@ -1499,11 +1417,7 @@ class TestRegistrantNameservers(MockEppLib):
call( call(
commands.UpdateHost( commands.UpdateHost(
name="ns2.nameserverwithip.gov", name="ns2.nameserverwithip.gov",
add=[ add=[common.Ip(addr="2001:0db8:85a3:0000:0000:8a2e:0370:7334", ip="v6")],
common.Ip(
addr="2001:0db8:85a3:0000:0000:8a2e:0370:7334", ip="v6"
)
],
rem=[], rem=[],
chg=None, chg=None,
), ),
@ -1565,9 +1479,7 @@ class TestRegistrantNameservers(MockEppLib):
self.assertEqual(self.mockedSendFunction.call_count, 4) self.assertEqual(self.mockedSendFunction.call_count, 4)
def test_is_subdomain_with_no_ip(self): def test_is_subdomain_with_no_ip(self):
domain, _ = Domain.objects.get_or_create( domain, _ = Domain.objects.get_or_create(name="nameserversubdomain.gov", state=Domain.State.READY)
name="nameserversubdomain.gov", state=Domain.State.READY
)
with self.assertRaises(NameserverError): with self.assertRaises(NameserverError):
domain.nameservers = [ domain.nameservers = [
@ -1576,9 +1488,7 @@ class TestRegistrantNameservers(MockEppLib):
] ]
def test_not_subdomain_but_has_ip(self): def test_not_subdomain_but_has_ip(self):
domain, _ = Domain.objects.get_or_create( domain, _ = Domain.objects.get_or_create(name="nameserversubdomain.gov", state=Domain.State.READY)
name="nameserversubdomain.gov", state=Domain.State.READY
)
with self.assertRaises(NameserverError): with self.assertRaises(NameserverError):
domain.nameservers = [ domain.nameservers = [
@ -1587,9 +1497,7 @@ class TestRegistrantNameservers(MockEppLib):
] ]
def test_is_subdomain_but_ip_addr_not_valid(self): def test_is_subdomain_but_ip_addr_not_valid(self):
domain, _ = Domain.objects.get_or_create( domain, _ = Domain.objects.get_or_create(name="nameserversubdomain.gov", state=Domain.State.READY)
name="nameserversubdomain.gov", state=Domain.State.READY
)
with self.assertRaises(NameserverError): with self.assertRaises(NameserverError):
domain.nameservers = [ domain.nameservers = [
@ -1600,9 +1508,7 @@ class TestRegistrantNameservers(MockEppLib):
def test_setting_not_allowed(self): def test_setting_not_allowed(self):
"""Scenario: A domain state is not Ready or DNS Needed """Scenario: A domain state is not Ready or DNS Needed
then setting nameservers is not allowed""" then setting nameservers is not allowed"""
domain, _ = Domain.objects.get_or_create( domain, _ = Domain.objects.get_or_create(name="onholdDomain.gov", state=Domain.State.ON_HOLD)
name="onholdDomain.gov", state=Domain.State.ON_HOLD
)
with self.assertRaises(ActionNotAllowed): with self.assertRaises(ActionNotAllowed):
domain.nameservers = [self.nameserver1, self.nameserver2] domain.nameservers = [self.nameserver1, self.nameserver2]
@ -1618,9 +1524,7 @@ class TestRegistrantNameservers(MockEppLib):
don't want to lose user info (and exit out too early) don't want to lose user info (and exit out too early)
""" """
domain, _ = Domain.objects.get_or_create( domain, _ = Domain.objects.get_or_create(name="failednameserver.gov", state=Domain.State.READY)
name="failednameserver.gov", state=Domain.State.READY
)
with self.assertRaises(RegistryError): with self.assertRaises(RegistryError):
domain.nameservers = [("ns1.failednameserver.gov", ["4.5.6"])] domain.nameservers = [("ns1.failednameserver.gov", ["4.5.6"])]
@ -1647,9 +1551,7 @@ class TestNameserverValidation(TestCase):
def test_64_char_label_too_long(self): def test_64_char_label_too_long(self):
"""Test that label of 64 characters or longer is invalid""" """Test that label of 64 characters or longer is invalid"""
label_too_long = ( label_too_long = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
)
domain_label_too_long = "www." + label_too_long + ".gov" domain_label_too_long = "www." + label_too_long + ".gov"
self.assertFalse(Domain.isValidHost(domain_label_too_long)) self.assertFalse(Domain.isValidHost(domain_label_too_long))
@ -1684,9 +1586,7 @@ class TestRegistrantDNSSEC(MockEppLib):
"""Rule: Registrants may modify their secure DNS data""" """Rule: Registrants may modify their secure DNS data"""
# helper function to create UpdateDomainDNSSECExtention object for verification # helper function to create UpdateDomainDNSSECExtention object for verification
def createUpdateExtension( def createUpdateExtension(self, dnssecdata: extensions.DNSSECExtension, remove=False):
self, dnssecdata: extensions.DNSSECExtension, remove=False
):
if not remove: if not remove:
return commands.UpdateDomainDNSSECExtension( return commands.UpdateDomainDNSSECExtension(
maxSigLife=dnssecdata.maxSigLife, maxSigLife=dnssecdata.maxSigLife,
@ -1946,9 +1846,7 @@ class TestRegistrantDNSSEC(MockEppLib):
] ]
) )
self.assertEquals( self.assertEquals(dnssecdata_get.dsData, self.dnssecExtensionWithMultDsData.dsData)
dnssecdata_get.dsData, self.dnssecExtensionWithMultDsData.dsData
)
patcher.stop() patcher.stop()
@ -2052,9 +1950,7 @@ class TestRegistrantDNSSEC(MockEppLib):
with self.assertRaises(RegistryError) as err: with self.assertRaises(RegistryError) as err:
domain.dnssecdata = self.dnssecExtensionWithDsData domain.dnssecdata = self.dnssecExtensionWithDsData
self.assertTrue( self.assertTrue(err.is_client_error() or err.is_session_error() or err.is_server_error())
err.is_client_error() or err.is_session_error() or err.is_server_error()
)
class TestAnalystClientHold(MockEppLib): class TestAnalystClientHold(MockEppLib):
@ -2068,13 +1964,9 @@ class TestAnalystClientHold(MockEppLib):
""" """
super().setUp() super().setUp()
# for the tests, need a domain in the ready state # for the tests, need a domain in the ready state
self.domain, _ = Domain.objects.get_or_create( self.domain, _ = Domain.objects.get_or_create(name="fake.gov", state=Domain.State.READY)
name="fake.gov", state=Domain.State.READY
)
# for the tests, need a domain in the on_hold state # for the tests, need a domain in the on_hold state
self.domain_on_hold, _ = Domain.objects.get_or_create( self.domain_on_hold, _ = Domain.objects.get_or_create(name="fake-on-hold.gov", state=Domain.State.ON_HOLD)
name="fake-on-hold.gov", state=Domain.State.ON_HOLD
)
def tearDown(self): def tearDown(self):
Domain.objects.all().delete() Domain.objects.all().delete()
@ -2222,9 +2114,7 @@ class TestAnalystClientHold(MockEppLib):
# is_server_error; so test for those conditions # is_server_error; so test for those conditions
with self.assertRaises(RegistryError) as err: with self.assertRaises(RegistryError) as err:
self.domain.place_client_hold() self.domain.place_client_hold()
self.assertTrue( self.assertTrue(err.is_client_error() or err.is_session_error() or err.is_server_error())
err.is_client_error() or err.is_session_error() or err.is_server_error()
)
patcher.stop() patcher.stop()
@ -2303,12 +2193,8 @@ class TestAnalystDelete(MockEppLib):
And a domain exists in the registry And a domain exists in the registry
""" """
super().setUp() super().setUp()
self.domain, _ = Domain.objects.get_or_create( self.domain, _ = Domain.objects.get_or_create(name="fake.gov", state=Domain.State.READY)
name="fake.gov", state=Domain.State.READY self.domain_on_hold, _ = Domain.objects.get_or_create(name="fake-on-hold.gov", state=Domain.State.ON_HOLD)
)
self.domain_on_hold, _ = Domain.objects.get_or_create(
name="fake-on-hold.gov", state=Domain.State.ON_HOLD
)
def tearDown(self): def tearDown(self):
Domain.objects.all().delete() Domain.objects.all().delete()
@ -2351,19 +2237,14 @@ class TestAnalystDelete(MockEppLib):
And `state` is not set to `DELETED` And `state` is not set to `DELETED`
""" """
# Desired domain # Desired domain
domain, _ = Domain.objects.get_or_create( domain, _ = Domain.objects.get_or_create(name="failDelete.gov", state=Domain.State.ON_HOLD)
name="failDelete.gov", state=Domain.State.ON_HOLD
)
# Put the domain in client hold # Put the domain in client hold
domain.place_client_hold() domain.place_client_hold()
# Delete it # Delete it
with self.assertRaises(RegistryError) as err: with self.assertRaises(RegistryError) as err:
domain.deletedInEpp() domain.deletedInEpp()
self.assertTrue( self.assertTrue(err.is_client_error() and err.code == ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION)
err.is_client_error()
and err.code == ErrorCode.OBJECT_ASSOCIATION_PROHIBITS_OPERATION
)
self.mockedSendFunction.assert_has_calls( self.mockedSendFunction.assert_has_calls(
[ [
call( call(
@ -2390,10 +2271,7 @@ class TestAnalystDelete(MockEppLib):
self.assertEqual(self.domain.state, Domain.State.READY) self.assertEqual(self.domain.state, Domain.State.READY)
with self.assertRaises(TransitionNotAllowed) as err: with self.assertRaises(TransitionNotAllowed) as err:
self.domain.deletedInEpp() self.domain.deletedInEpp()
self.assertTrue( self.assertTrue(err.is_client_error() and err.code == ErrorCode.OBJECT_STATUS_PROHIBITS_OPERATION)
err.is_client_error()
and err.code == ErrorCode.OBJECT_STATUS_PROHIBITS_OPERATION
)
# Domain should not be deleted # Domain should not be deleted
self.assertNotEqual(self.domain, None) self.assertNotEqual(self.domain, None)
# Domain should have the right state # Domain should have the right state

View file

@ -12,9 +12,7 @@ class TestNameserverError(TestCase):
nameserver = "nameserver val" nameserver = "nameserver val"
expected = "Using your domain for a name server requires an IP address" expected = "Using your domain for a name server requires an IP address"
nsException = NameserverError( nsException = NameserverError(code=nsErrorCodes.MISSING_IP, nameserver=nameserver)
code=nsErrorCodes.MISSING_IP, nameserver=nameserver
)
self.assertEqual(nsException.message, expected) self.assertEqual(nsException.message, expected)
self.assertEqual(nsException.code, nsErrorCodes.MISSING_IP) self.assertEqual(nsException.code, nsErrorCodes.MISSING_IP)
@ -24,9 +22,7 @@ class TestNameserverError(TestCase):
nameserver = "nameserver val" nameserver = "nameserver val"
expected = "Too many hosts provided, you may not have more than 13 nameservers." expected = "Too many hosts provided, you may not have more than 13 nameservers."
nsException = NameserverError( nsException = NameserverError(code=nsErrorCodes.TOO_MANY_HOSTS, nameserver=nameserver)
code=nsErrorCodes.TOO_MANY_HOSTS, nameserver=nameserver
)
self.assertEqual(nsException.message, expected) self.assertEqual(nsException.message, expected)
self.assertEqual(nsException.code, nsErrorCodes.TOO_MANY_HOSTS) self.assertEqual(nsException.code, nsErrorCodes.TOO_MANY_HOSTS)
@ -36,8 +32,6 @@ class TestNameserverError(TestCase):
nameserver = "nameserver val" nameserver = "nameserver val"
expected = f"{nameserver}: Enter an IP address in the required format." expected = f"{nameserver}: Enter an IP address in the required format."
nsException = NameserverError( nsException = NameserverError(code=nsErrorCodes.INVALID_IP, nameserver=nameserver, ip=ip)
code=nsErrorCodes.INVALID_IP, nameserver=nameserver, ip=ip
)
self.assertEqual(nsException.message, expected) self.assertEqual(nsException.message, expected)
self.assertEqual(nsException.code, nsErrorCodes.INVALID_IP) self.assertEqual(nsException.code, nsErrorCodes.INVALID_IP)

View file

@ -18,21 +18,11 @@ class ExportDataTest(TestCase):
username=username, first_name=first_name, last_name=last_name, email=email username=username, first_name=first_name, last_name=last_name, email=email
) )
self.domain_1, _ = Domain.objects.get_or_create( self.domain_1, _ = Domain.objects.get_or_create(name="cdomain1.gov", state=Domain.State.READY)
name="cdomain1.gov", state=Domain.State.READY self.domain_2, _ = Domain.objects.get_or_create(name="adomain2.gov", state=Domain.State.DNS_NEEDED)
) self.domain_3, _ = Domain.objects.get_or_create(name="ddomain3.gov", state=Domain.State.ON_HOLD)
self.domain_2, _ = Domain.objects.get_or_create( self.domain_4, _ = Domain.objects.get_or_create(name="bdomain4.gov", state=Domain.State.UNKNOWN)
name="adomain2.gov", state=Domain.State.DNS_NEEDED self.domain_4, _ = Domain.objects.get_or_create(name="bdomain4.gov", state=Domain.State.UNKNOWN)
)
self.domain_3, _ = Domain.objects.get_or_create(
name="ddomain3.gov", state=Domain.State.ON_HOLD
)
self.domain_4, _ = Domain.objects.get_or_create(
name="bdomain4.gov", state=Domain.State.UNKNOWN
)
self.domain_4, _ = Domain.objects.get_or_create(
name="bdomain4.gov", state=Domain.State.UNKNOWN
)
self.domain_information_1, _ = DomainInformation.objects.get_or_create( self.domain_information_1, _ = DomainInformation.objects.get_or_create(
creator=self.user, creator=self.user,
@ -121,16 +111,8 @@ class ExportDataTest(TestCase):
# Normalize line endings and remove commas, # Normalize line endings and remove commas,
# spaces and leading/trailing whitespace # spaces and leading/trailing whitespace
csv_content = ( csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
csv_content.replace(",,", "") expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
.replace(",", "")
.replace(" ", "")
.replace("\r\n", "\n")
.strip()
)
expected_content = (
expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
)
self.assertEqual(csv_content, expected_content) self.assertEqual(csv_content, expected_content)
@ -181,15 +163,7 @@ class ExportDataTest(TestCase):
# Normalize line endings and remove commas, # Normalize line endings and remove commas,
# spaces and leading/trailing whitespace # spaces and leading/trailing whitespace
csv_content = ( csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
csv_content.replace(",,", "") expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
.replace(",", "")
.replace(" ", "")
.replace("\r\n", "\n")
.strip()
)
expected_content = (
expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
)
self.assertEqual(csv_content, expected_content) self.assertEqual(csv_content, expected_content)

View file

@ -58,9 +58,7 @@ class TestUserPostSave(TestCase):
"""Expect 1 Contact containing data copied from User.""" """Expect 1 Contact containing data copied from User."""
# create the user # create the user
self.assertEqual(len(Contact.objects.all()), 0) self.assertEqual(len(Contact.objects.all()), 0)
user = get_user_model().objects.create( user = get_user_model().objects.create(username=self.username, first_name="", last_name="", email="", phone="")
username=self.username, first_name="", last_name="", email="", phone=""
)
# delete the contact # delete the contact
Contact.objects.all().delete() Contact.objects.all().delete()
self.assertEqual(len(Contact.objects.all()), 0) self.assertEqual(len(Contact.objects.all()), 0)

View file

@ -23,16 +23,12 @@ class TestTemplateTags(TestCase):
return Template(string).render(context) return Template(string).render(context)
def test_public_site_url(self): def test_public_site_url(self):
result = self._render_template( result = self._render_template("{% load url_helpers %}{% public_site_url 'directory/page' %}")
"{% load url_helpers %}{% public_site_url 'directory/page' %}"
)
self.assertTrue(result.startswith(settings.GETGOV_PUBLIC_SITE_URL)) self.assertTrue(result.startswith(settings.GETGOV_PUBLIC_SITE_URL))
self.assertTrue(result.endswith("/directory/page")) self.assertTrue(result.endswith("/directory/page"))
def test_public_site_url_leading_slash(self): def test_public_site_url_leading_slash(self):
result = self._render_template( result = self._render_template("{% load url_helpers %}{% public_site_url '/directory/page' %}")
"{% load url_helpers %}{% public_site_url '/directory/page' %}"
)
self.assertTrue(result.startswith(settings.GETGOV_PUBLIC_SITE_URL)) self.assertTrue(result.startswith(settings.GETGOV_PUBLIC_SITE_URL))
# slash-slash host slash directory slash page # slash-slash host slash directory slash page
self.assertEqual(result.count("/"), 4) self.assertEqual(result.count("/"), 4)
@ -40,17 +36,11 @@ class TestTemplateTags(TestCase):
class CustomFiltersTestCase(TestCase): class CustomFiltersTestCase(TestCase):
def test_extract_value_filter(self): def test_extract_value_filter(self):
html_input = ( html_input = '<input type="checkbox" name="_selected_action" value="123" id="label_123" class="action-select">'
'<input type="checkbox" name="_selected_action" value="123" '
'id="label_123" class="action-select">'
)
result = extract_value(html_input) result = extract_value(html_input)
self.assertEqual(result, "123") self.assertEqual(result, "123")
html_input = ( html_input = '<input type="checkbox" name="_selected_action" value="abc" id="label_123" class="action-select">'
'<input type="checkbox" name="_selected_action" value="abc" '
'id="label_123" class="action-select">'
)
result = extract_value(html_input) result = extract_value(html_input)
self.assertEqual(result, "abc") self.assertEqual(result, "abc")
@ -81,9 +71,7 @@ class CustomFiltersTestCase(TestCase):
substring = "XYZ" substring = "XYZ"
result = slice_after(value, substring) result = slice_after(value, substring)
self.assertEqual( self.assertEqual(result, value) # Should return the original value if substring not found
result, value
) # Should return the original value if substring not found
def test_contains_checkbox_with_checkbox(self): def test_contains_checkbox_with_checkbox(self):
# Test the filter when HTML list contains a checkbox # Test the filter when HTML list contains a checkbox

View file

@ -1,4 +1,7 @@
import datetime import datetime
from io import StringIO
from django.test import TestCase from django.test import TestCase
from registrar.models import ( from registrar.models import (
@ -13,6 +16,8 @@ from registrar.models import (
from django.core.management import call_command from django.core.management import call_command
from unittest.mock import patch from unittest.mock import patch
from .common import less_console_noise
class TestMigrations(TestCase): class TestMigrations(TestCase):
def setUp(self): def setUp(self):
@ -103,9 +108,7 @@ class TestMigrations(TestCase):
# Check Domain table # Check Domain table
matching_domains = Domain.objects.filter(name=transition_domain_name) matching_domains = Domain.objects.filter(name=transition_domain_name)
# Check Domain Information table # Check Domain Information table
matching_domain_informations = DomainInformation.objects.filter( matching_domain_informations = DomainInformation.objects.filter(domain__name=transition_domain_name)
domain__name=transition_domain_name
)
# Check Domain Invitation table # Check Domain Invitation table
matching_domain_invitations = DomainInvitation.objects.filter( matching_domain_invitations = DomainInvitation.objects.filter(
email=transition_domain_email.lower(), email=transition_domain_email.lower(),
@ -146,12 +149,8 @@ class TestMigrations(TestCase):
) )
self.assertEqual(total_missing_domains, expected_missing_domains) self.assertEqual(total_missing_domains, expected_missing_domains)
self.assertEqual(total_duplicate_domains, expected_duplicate_domains) self.assertEqual(total_duplicate_domains, expected_duplicate_domains)
self.assertEqual( self.assertEqual(total_missing_domain_informations, expected_missing_domain_informations)
total_missing_domain_informations, expected_missing_domain_informations self.assertEqual(total_missing_domain_invitations, expected_missing_domain_invitations)
)
self.assertEqual(
total_missing_domain_invitations, expected_missing_domain_invitations
)
self.assertEqual(total_transition_domains, expected_total_transition_domains) self.assertEqual(total_transition_domains, expected_total_transition_domains)
self.assertEqual(total_domains, expected_total_domains) self.assertEqual(total_domains, expected_total_domains)
@ -352,10 +351,8 @@ class TestMigrations(TestCase):
# Simluate Logins # Simluate Logins
for invite in DomainInvitation.objects.all(): for invite in DomainInvitation.objects.all():
# get a user with this email address # get a user with this email address
user, user_created = User.objects.get_or_create( user, user_created = User.objects.get_or_create(email=invite.email, username=invite.email)
email=invite.email, username=invite.email user.on_each_login()
)
user.first_login()
# Analyze the tables # Analyze the tables
expected_total_transition_domains = 9 expected_total_transition_domains = 9
@ -377,3 +374,42 @@ class TestMigrations(TestCase):
expected_missing_domain_informations, expected_missing_domain_informations,
expected_missing_domain_invitations, expected_missing_domain_invitations,
) )
def test_send_domain_invitations_email(self):
"""Can send only a single domain invitation email."""
with less_console_noise():
self.run_load_domains()
self.run_transfer_domains()
# this is one of the email addresses in data/test_contacts.txt
output_stream = StringIO()
# also have to re-point the logging handlers to output_stream
with less_console_noise(output_stream):
call_command("send_domain_invitations", "testuser@gmail.com", stdout=output_stream)
# Check that we had the right numbers in our output
output = output_stream.getvalue()
# should only be one domain we send email for
self.assertIn("Found 1 transition domains", output)
self.assertTrue("would send email to testuser@gmail.com", output)
def test_send_domain_invitations_two_emails(self):
"""Can send only a single domain invitation email."""
with less_console_noise():
self.run_load_domains()
self.run_transfer_domains()
# these are two email addresses in data/test_contacts.txt
output_stream = StringIO()
# also have to re-point the logging handlers to output_stream
with less_console_noise(output_stream):
call_command(
"send_domain_invitations", "testuser@gmail.com", "agustina.wyman7@test.com", stdout=output_stream
)
# Check that we had the right numbers in our output
output = output_stream.getvalue()
# should only be one domain we send email for
self.assertIn("Found 2 transition domains", output)
self.assertTrue("would send email to testuser@gmail.com", output)
self.assertTrue("would send email to agustina.wyman7@test.com", output)

View file

@ -52,9 +52,7 @@ def iter_patterns(urlconf, patterns=None, namespace=None):
if isinstance(pattern, URLPattern): if isinstance(pattern, URLPattern):
viewname = pattern.name viewname = pattern.name
if viewname is None and namespace not in NAMESPACES_WITH_UNNAMED_VIEWS: if viewname is None and namespace not in NAMESPACES_WITH_UNNAMED_VIEWS:
raise AssertionError( raise AssertionError(f"namespace {namespace} cannot contain unnamed views")
f"namespace {namespace} cannot contain unnamed views"
)
if namespace and viewname is not None: if namespace and viewname is not None:
viewname = f"{namespace}:{viewname}" viewname = f"{namespace}:{viewname}"
yield (viewname, pattern.pattern) yield (viewname, pattern.pattern)
@ -65,9 +63,7 @@ def iter_patterns(urlconf, patterns=None, namespace=None):
raise AssertionError("nested namespaces are not currently supported") raise AssertionError("nested namespaces are not currently supported")
if pattern.namespace in IGNORE_NAMESPACES: if pattern.namespace in IGNORE_NAMESPACES:
continue continue
yield from iter_patterns( yield from iter_patterns(urlconf, pattern.url_patterns, namespace or pattern.namespace)
urlconf, pattern.url_patterns, namespace or pattern.namespace
)
else: else:
raise AssertionError("unknown pattern class") raise AssertionError("unknown pattern class")
@ -91,9 +87,7 @@ def iter_sample_urls(urlconf):
for kwarg in named_groups: for kwarg in named_groups:
if kwarg not in SAMPLE_KWARGS: if kwarg not in SAMPLE_KWARGS:
raise AssertionError( raise AssertionError(f'Sample value for {kwarg} in pattern "{route}" not found')
f'Sample value for {kwarg} in pattern "{route}" not found'
)
kwargs[kwarg] = SAMPLE_KWARGS[kwarg] kwargs[kwarg] = SAMPLE_KWARGS[kwarg]
url = reverse(viewname, args=args, kwargs=kwargs) url = reverse(viewname, args=args, kwargs=kwargs)
@ -141,15 +135,13 @@ class TestURLAuth(TestCase):
self.assertRegex( self.assertRegex(
redirect, redirect,
r"^\/openid\/login", r"^\/openid\/login",
f"GET {url} should redirect to login or deny access, but instead " f"GET {url} should redirect to login or deny access, but instead " f"it redirects to {redirect}",
f"it redirects to {redirect}",
) )
elif code == 401 or code == 403: elif code == 401 or code == 403:
pass pass
else: else:
raise AssertionError( raise AssertionError(
f"GET {url} returned HTTP {code}, but should redirect to login or " f"GET {url} returned HTTP {code}, but should redirect to login or deny access",
"deny access",
) )
def test_login_required_all_urls(self): def test_login_required_all_urls(self):

View file

@ -79,9 +79,7 @@ class LoggedInTests(TestWithUser):
response = self.client.get("/") response = self.client.get("/")
self.assertNotContains(response, "igorville.gov") self.assertNotContains(response, "igorville.gov")
site = DraftDomain.objects.create(name="igorville.gov") site = DraftDomain.objects.create(name="igorville.gov")
application = DomainApplication.objects.create( application = DomainApplication.objects.create(creator=self.user, requested_domain=site)
creator=self.user, requested_domain=site
)
response = self.client.get("/") response = self.client.get("/")
# count = 2 because it is also in screenreader content # count = 2 because it is also in screenreader content
self.assertContains(response, "igorville.gov", count=2) self.assertContains(response, "igorville.gov", count=2)
@ -92,9 +90,7 @@ class LoggedInTests(TestWithUser):
response = self.client.get("/") response = self.client.get("/")
domain, _ = Domain.objects.get_or_create(name="igorville.gov") domain, _ = Domain.objects.get_or_create(name="igorville.gov")
self.assertNotContains(response, "igorville.gov") self.assertNotContains(response, "igorville.gov")
role, _ = UserDomainRole.objects.get_or_create( role, _ = UserDomainRole.objects.get_or_create(user=self.user, domain=domain, role=UserDomainRole.Roles.MANAGER)
user=self.user, domain=domain, role=UserDomainRole.Roles.MANAGER
)
response = self.client.get("/") response = self.client.get("/")
# count = 2 because it is also in screenreader content # count = 2 because it is also in screenreader content
self.assertContains(response, "igorville.gov", count=2) self.assertContains(response, "igorville.gov", count=2)
@ -140,9 +136,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
page = self.app.get(reverse("application:")).follow() page = self.app.get(reverse("application:")).follow()
# submitting should get back the same page if the required field is empty # submitting should get back the same page if the required field is empty
result = page.form.submit() result = page.form.submit()
self.assertIn( self.assertIn("What kind of U.S.-based government organization do you represent?", result)
"What kind of U.S.-based government organization do you represent?", result
)
@boto3_mocking.patching @boto3_mocking.patching
def test_application_form_submission(self): def test_application_form_submission(self):
@ -206,9 +200,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
org_contact_page = federal_result.follow() org_contact_page = federal_result.follow()
org_contact_form = org_contact_page.form org_contact_form = org_contact_page.form
# federal agency so we have to fill in federal_agency # federal agency so we have to fill in federal_agency
org_contact_form[ org_contact_form["organization_contact-federal_agency"] = "General Services Administration"
"organization_contact-federal_agency"
] = "General Services Administration"
org_contact_form["organization_contact-organization_name"] = "Testorg" org_contact_form["organization_contact-organization_name"] = "Testorg"
org_contact_form["organization_contact-address_line1"] = "address 1" org_contact_form["organization_contact-address_line1"] = "address 1"
org_contact_form["organization_contact-address_line2"] = "address 2" org_contact_form["organization_contact-address_line2"] = "address 2"
@ -232,9 +224,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
# the post request should return a redirect to the next form in # the post request should return a redirect to the next form in
# the application # the application
self.assertEqual(org_contact_result.status_code, 302) self.assertEqual(org_contact_result.status_code, 302)
self.assertEqual( self.assertEqual(org_contact_result["Location"], "/register/authorizing_official/")
org_contact_result["Location"], "/register/authorizing_official/"
)
num_pages_tested += 1 num_pages_tested += 1
# ---- AUTHORIZING OFFICIAL PAGE ---- # ---- AUTHORIZING OFFICIAL PAGE ----
@ -299,9 +289,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
# validate that data from this step are being saved # validate that data from this step are being saved
application = DomainApplication.objects.get() # there's only one application = DomainApplication.objects.get() # there's only one
self.assertEqual(application.requested_domain.name, "city.gov") self.assertEqual(application.requested_domain.name, "city.gov")
self.assertEqual( self.assertEqual(application.alternative_domains.filter(website="city1.gov").count(), 1)
application.alternative_domains.filter(website="city1.gov").count(), 1
)
# the post request should return a redirect to the next form in # the post request should return a redirect to the next form in
# the application # the application
self.assertEqual(dotgov_result.status_code, 302) self.assertEqual(dotgov_result.status_code, 302)
@ -649,9 +637,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
type_form = type_page.form type_form = type_page.form
type_form[ type_form["organization_type-organization_type"] = DomainApplication.OrganizationChoices.INTERSTATE
"organization_type-organization_type"
] = DomainApplication.OrganizationChoices.INTERSTATE
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
type_result = type_page.form.submit() type_result = type_page.form.submit()
@ -675,9 +661,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
# the post request should return a redirect to the # the post request should return a redirect to the
# about your organization page if it was successful. # about your organization page if it was successful.
self.assertEqual(contact_result.status_code, 302) self.assertEqual(contact_result.status_code, 302)
self.assertEqual( self.assertEqual(contact_result["Location"], "/register/about_your_organization/")
contact_result["Location"], "/register/about_your_organization/"
)
def test_application_about_your_organization_special(self): def test_application_about_your_organization_special(self):
"""Special districts have to answer an additional question.""" """Special districts have to answer an additional question."""
@ -689,9 +673,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
type_form = type_page.form type_form = type_page.form
type_form[ type_form["organization_type-organization_type"] = DomainApplication.OrganizationChoices.SPECIAL_DISTRICT
"organization_type-organization_type"
] = DomainApplication.OrganizationChoices.SPECIAL_DISTRICT
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
type_result = type_page.form.submit() type_result = type_page.form.submit()
# follow first redirect # follow first redirect
@ -728,9 +710,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
type_form = type_page.form type_form = type_page.form
type_form[ type_form["organization_type-organization_type"] = DomainApplication.OrganizationChoices.INTERSTATE
"organization_type-organization_type"
] = DomainApplication.OrganizationChoices.INTERSTATE
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
type_result = type_page.form.submit() type_result = type_page.form.submit()
# follow first redirect # follow first redirect
@ -748,9 +728,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
# and then setting the cookie on each request. # and then setting the cookie on each request.
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
type_form = type_page.form type_form = type_page.form
type_form[ type_form["organization_type-organization_type"] = DomainApplication.OrganizationChoices.TRIBAL
"organization_type-organization_type"
] = DomainApplication.OrganizationChoices.TRIBAL
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
type_result = type_page.form.submit() type_result = type_page.form.submit()
# the tribal government page comes immediately afterwards # the tribal government page comes immediately afterwards
@ -793,9 +771,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
org_contact_page = federal_result.follow() org_contact_page = federal_result.follow()
org_contact_form = org_contact_page.form org_contact_form = org_contact_page.form
# federal agency so we have to fill in federal_agency # federal agency so we have to fill in federal_agency
org_contact_form[ org_contact_form["organization_contact-federal_agency"] = "General Services Administration"
"organization_contact-federal_agency"
] = "General Services Administration"
org_contact_form["organization_contact-organization_name"] = "Testorg" org_contact_form["organization_contact-organization_name"] = "Testorg"
org_contact_form["organization_contact-address_line1"] = "address 1" org_contact_form["organization_contact-address_line1"] = "address 1"
org_contact_form["organization_contact-address_line2"] = "address 2" org_contact_form["organization_contact-address_line2"] = "address 2"
@ -856,9 +832,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
org_contact_page = federal_result.follow() org_contact_page = federal_result.follow()
org_contact_form = org_contact_page.form org_contact_form = org_contact_page.form
# federal agency so we have to fill in federal_agency # federal agency so we have to fill in federal_agency
org_contact_form[ org_contact_form["organization_contact-federal_agency"] = "General Services Administration"
"organization_contact-federal_agency"
] = "General Services Administration"
org_contact_form["organization_contact-organization_name"] = "Testorg" org_contact_form["organization_contact-organization_name"] = "Testorg"
org_contact_form["organization_contact-address_line1"] = "address 1" org_contact_form["organization_contact-address_line1"] = "address 1"
org_contact_form["organization_contact-address_line2"] = "address 2" org_contact_form["organization_contact-address_line2"] = "address 2"
@ -1099,45 +1073,23 @@ class TestWithDomainPermissions(TestWithUser):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.domain, _ = Domain.objects.get_or_create(name="igorville.gov") self.domain, _ = Domain.objects.get_or_create(name="igorville.gov")
self.domain_with_ip, _ = Domain.objects.get_or_create( self.domain_with_ip, _ = Domain.objects.get_or_create(name="nameserverwithip.gov")
name="nameserverwithip.gov" self.domain_just_nameserver, _ = Domain.objects.get_or_create(name="justnameserver.com")
) self.domain_no_information, _ = Domain.objects.get_or_create(name="noinformation.gov")
self.domain_just_nameserver, _ = Domain.objects.get_or_create(
name="justnameserver.com"
)
self.domain_no_information, _ = Domain.objects.get_or_create(
name="noinformation.gov"
)
self.domain_dsdata, _ = Domain.objects.get_or_create(name="dnssec-dsdata.gov") self.domain_dsdata, _ = Domain.objects.get_or_create(name="dnssec-dsdata.gov")
self.domain_multdsdata, _ = Domain.objects.get_or_create( self.domain_multdsdata, _ = Domain.objects.get_or_create(name="dnssec-multdsdata.gov")
name="dnssec-multdsdata.gov"
)
# We could simply use domain (igorville) but this will be more readable in tests # We could simply use domain (igorville) but this will be more readable in tests
# that inherit this setUp # that inherit this setUp
self.domain_dnssec_none, _ = Domain.objects.get_or_create( self.domain_dnssec_none, _ = Domain.objects.get_or_create(name="dnssec-none.gov")
name="dnssec-none.gov"
)
self.domain_information, _ = DomainInformation.objects.get_or_create( self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
creator=self.user, domain=self.domain
)
DomainInformation.objects.get_or_create( DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain_dsdata)
creator=self.user, domain=self.domain_dsdata DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain_multdsdata)
) DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain_dnssec_none)
DomainInformation.objects.get_or_create( DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain_with_ip)
creator=self.user, domain=self.domain_multdsdata DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain_just_nameserver)
)
DomainInformation.objects.get_or_create(
creator=self.user, domain=self.domain_dnssec_none
)
DomainInformation.objects.get_or_create(
creator=self.user, domain=self.domain_with_ip
)
DomainInformation.objects.get_or_create(
creator=self.user, domain=self.domain_just_nameserver
)
self.role, _ = UserDomainRole.objects.get_or_create( self.role, _ = UserDomainRole.objects.get_or_create(
user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER
@ -1196,9 +1148,7 @@ class TestDomainPermissions(TestWithDomainPermissions):
"domain-security-email", "domain-security-email",
]: ]:
with self.subTest(view_name=view_name): with self.subTest(view_name=view_name):
response = self.client.get( response = self.client.get(reverse(view_name, kwargs={"pk": self.domain.id}))
reverse(view_name, kwargs={"pk": self.domain.id})
)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
def test_no_domain_role(self): def test_no_domain_role(self):
@ -1218,9 +1168,7 @@ class TestDomainPermissions(TestWithDomainPermissions):
]: ]:
with self.subTest(view_name=view_name): with self.subTest(view_name=view_name):
with less_console_noise(): with less_console_noise():
response = self.client.get( response = self.client.get(reverse(view_name, kwargs={"pk": self.domain.id}))
reverse(view_name, kwargs={"pk": self.domain.id})
)
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
@ -1255,9 +1203,7 @@ class TestDomainOverview(TestWithDomainPermissions, WebTest):
self.assertContains(home_page, "justnameserver.com") self.assertContains(home_page, "justnameserver.com")
# View nameserver on Domain Overview page # View nameserver on Domain Overview page
detail_page = self.app.get( detail_page = self.app.get(reverse("domain", kwargs={"pk": self.domain_just_nameserver.id}))
reverse("domain", kwargs={"pk": self.domain_just_nameserver.id})
)
self.assertContains(detail_page, "justnameserver.com") self.assertContains(detail_page, "justnameserver.com")
self.assertContains(detail_page, "ns1.justnameserver.com") self.assertContains(detail_page, "ns1.justnameserver.com")
@ -1268,9 +1214,7 @@ class TestDomainOverview(TestWithDomainPermissions, WebTest):
self.assertContains(home_page, "nameserverwithip.gov") self.assertContains(home_page, "nameserverwithip.gov")
# View nameserver on Domain Overview page # View nameserver on Domain Overview page
detail_page = self.app.get( detail_page = self.app.get(reverse("domain", kwargs={"pk": self.domain_with_ip.id}))
reverse("domain", kwargs={"pk": self.domain_with_ip.id})
)
self.assertContains(detail_page, "nameserverwithip.gov") self.assertContains(detail_page, "nameserverwithip.gov")
@ -1297,9 +1241,7 @@ class TestDomainOverview(TestWithDomainPermissions, WebTest):
session["analyst_action_location"] = self.domain_no_information.id session["analyst_action_location"] = self.domain_no_information.id
session.save() session.save()
detail_page = self.client.get( detail_page = self.client.get(reverse("domain", kwargs={"pk": self.domain_no_information.id}))
reverse("domain", kwargs={"pk": self.domain_no_information.id})
)
self.assertContains(detail_page, "noinformation.gov") self.assertContains(detail_page, "noinformation.gov")
self.assertContains(detail_page, "Domain missing domain information") self.assertContains(detail_page, "Domain missing domain information")
@ -1307,33 +1249,23 @@ class TestDomainOverview(TestWithDomainPermissions, WebTest):
class TestDomainManagers(TestDomainOverview): class TestDomainManagers(TestDomainOverview):
def test_domain_managers(self): def test_domain_managers(self):
response = self.client.get( response = self.client.get(reverse("domain-users", kwargs={"pk": self.domain.id}))
reverse("domain-users", kwargs={"pk": self.domain.id})
)
self.assertContains(response, "Domain managers") self.assertContains(response, "Domain managers")
def test_domain_managers_add_link(self): def test_domain_managers_add_link(self):
"""Button to get to user add page works.""" """Button to get to user add page works."""
management_page = self.app.get( management_page = self.app.get(reverse("domain-users", kwargs={"pk": self.domain.id}))
reverse("domain-users", kwargs={"pk": self.domain.id})
)
add_page = management_page.click("Add another user") add_page = management_page.click("Add another user")
self.assertContains(add_page, "Add another user") self.assertContains(add_page, "Add another user")
def test_domain_user_add(self): def test_domain_user_add(self):
response = self.client.get( response = self.client.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
reverse("domain-users-add", kwargs={"pk": self.domain.id})
)
self.assertContains(response, "Add another user") self.assertContains(response, "Add another user")
def test_domain_user_add_form(self): def test_domain_user_add_form(self):
"""Adding an existing user works.""" """Adding an existing user works."""
other_user, _ = get_user_model().objects.get_or_create( other_user, _ = get_user_model().objects.get_or_create(email="mayor@igorville.gov")
email="mayor@igorville.gov" add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
)
add_page = self.app.get(
reverse("domain-users-add", kwargs={"pk": self.domain.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = "mayor@igorville.gov" add_page.form["email"] = "mayor@igorville.gov"
@ -1362,13 +1294,9 @@ class TestDomainManagers(TestDomainOverview):
EMAIL = "mayor@igorville.gov" EMAIL = "mayor@igorville.gov"
User.objects.filter(email=EMAIL).delete() User.objects.filter(email=EMAIL).delete()
self.domain_information, _ = DomainInformation.objects.get_or_create( self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
creator=self.user, domain=self.domain
)
add_page = self.app.get( add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
reverse("domain-users-add", kwargs={"pk": self.domain.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = EMAIL add_page.form["email"] = EMAIL
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1387,16 +1315,12 @@ class TestDomainManagers(TestDomainOverview):
EMAIL = "mayor@igorville.gov" EMAIL = "mayor@igorville.gov"
User.objects.filter(email=EMAIL).delete() User.objects.filter(email=EMAIL).delete()
self.domain_information, _ = DomainInformation.objects.get_or_create( self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
creator=self.user, domain=self.domain
)
mock_client = MagicMock() mock_client = MagicMock()
mock_client_instance = mock_client.return_value mock_client_instance = mock_client.return_value
with boto3_mocking.clients.handler_for("sesv2", mock_client): with boto3_mocking.clients.handler_for("sesv2", mock_client):
add_page = self.app.get( add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
reverse("domain-users-add", kwargs={"pk": self.domain.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = EMAIL add_page.form["email"] = EMAIL
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1411,9 +1335,7 @@ class TestDomainManagers(TestDomainOverview):
def test_domain_invitation_cancel(self): def test_domain_invitation_cancel(self):
"""Posting to the delete view deletes an invitation.""" """Posting to the delete view deletes an invitation."""
EMAIL = "mayor@igorville.gov" EMAIL = "mayor@igorville.gov"
invitation, _ = DomainInvitation.objects.get_or_create( invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=EMAIL)
domain=self.domain, email=EMAIL
)
self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id})) self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id}))
with self.assertRaises(DomainInvitation.DoesNotExist): with self.assertRaises(DomainInvitation.DoesNotExist):
DomainInvitation.objects.get(id=invitation.id) DomainInvitation.objects.get(id=invitation.id)
@ -1421,17 +1343,13 @@ class TestDomainManagers(TestDomainOverview):
def test_domain_invitation_cancel_no_permissions(self): def test_domain_invitation_cancel_no_permissions(self):
"""Posting to the delete view as a different user should fail.""" """Posting to the delete view as a different user should fail."""
EMAIL = "mayor@igorville.gov" EMAIL = "mayor@igorville.gov"
invitation, _ = DomainInvitation.objects.get_or_create( invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=EMAIL)
domain=self.domain, email=EMAIL
)
other_user = User() other_user = User()
other_user.save() other_user.save()
self.client.force_login(other_user) self.client.force_login(other_user)
with less_console_noise(): # permission denied makes console errors with less_console_noise(): # permission denied makes console errors
result = self.client.post( result = self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id}))
reverse("invitation-delete", kwargs={"pk": invitation.id})
)
self.assertEqual(result.status_code, 403) self.assertEqual(result.status_code, 403)
@boto3_mocking.patching @boto3_mocking.patching
@ -1440,13 +1358,9 @@ class TestDomainManagers(TestDomainOverview):
EMAIL = "mayor@igorville.gov" EMAIL = "mayor@igorville.gov"
User.objects.filter(email=EMAIL).delete() User.objects.filter(email=EMAIL).delete()
add_page = self.app.get( add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
reverse("domain-users-add", kwargs={"pk": self.domain.id})
)
self.domain_information, _ = DomainInformation.objects.get_or_create( self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
creator=self.user, domain=self.domain
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = EMAIL add_page.form["email"] = EMAIL
@ -1457,8 +1371,8 @@ class TestDomainManagers(TestDomainOverview):
new_user = User.objects.create(username=EMAIL, email=EMAIL) new_user = User.objects.create(username=EMAIL, email=EMAIL)
# log them in to `self.app` # log them in to `self.app`
self.app.set_user(new_user.username) self.app.set_user(new_user.username)
# and manually call the first login callback # and manually call the on each login callback
new_user.first_login() new_user.on_each_login()
# Now load the home page and make sure our domain appears there # Now load the home page and make sure our domain appears there
home_page = self.app.get(reverse("home")) home_page = self.app.get(reverse("home"))
@ -1468,9 +1382,7 @@ class TestDomainManagers(TestDomainOverview):
class TestDomainNameservers(TestDomainOverview): class TestDomainNameservers(TestDomainOverview):
def test_domain_nameservers(self): def test_domain_nameservers(self):
"""Can load domain's nameservers page.""" """Can load domain's nameservers page."""
page = self.client.get( page = self.client.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id})
)
self.assertContains(page, "DNS name servers") self.assertContains(page, "DNS name servers")
def test_domain_nameservers_form_submit_one_nameserver(self): def test_domain_nameservers_form_submit_one_nameserver(self):
@ -1479,9 +1391,7 @@ class TestDomainNameservers(TestDomainOverview):
Uses self.app WebTest because we need to interact with forms. Uses self.app WebTest because we need to interact with forms.
""" """
# initial nameservers page has one server with two ips # initial nameservers page has one server with two ips
nameservers_page = self.app.get( nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# attempt to submit the form with only one nameserver, should error # attempt to submit the form with only one nameserver, should error
@ -1504,9 +1414,7 @@ class TestDomainNameservers(TestDomainOverview):
Uses self.app WebTest because we need to interact with forms. Uses self.app WebTest because we need to interact with forms.
""" """
# initial nameservers page has one server with two ips # initial nameservers page has one server with two ips
nameservers_page = self.app.get( nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# attempt to submit the form without two hosts, both subdomains, # attempt to submit the form without two hosts, both subdomains,
@ -1530,9 +1438,7 @@ class TestDomainNameservers(TestDomainOverview):
Uses self.app WebTest because we need to interact with forms. Uses self.app WebTest because we need to interact with forms.
""" """
# initial nameservers page has one server with two ips # initial nameservers page has one server with two ips
nameservers_page = self.app.get( nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# attempt to submit the form without two hosts, both subdomains, # attempt to submit the form without two hosts, both subdomains,
@ -1560,9 +1466,7 @@ class TestDomainNameservers(TestDomainOverview):
nameserver2 = "ns2.igorville.com" nameserver2 = "ns2.igorville.com"
valid_ip = "127.0.0.1" valid_ip = "127.0.0.1"
# initial nameservers page has one server with two ips # initial nameservers page has one server with two ips
nameservers_page = self.app.get( nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# attempt to submit the form without two hosts, both subdomains, # attempt to submit the form without two hosts, both subdomains,
@ -1590,9 +1494,7 @@ class TestDomainNameservers(TestDomainOverview):
nameserver = "ns2.igorville.gov" nameserver = "ns2.igorville.gov"
invalid_ip = "123" invalid_ip = "123"
# initial nameservers page has one server with two ips # initial nameservers page has one server with two ips
nameservers_page = self.app.get( nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# attempt to submit the form without two hosts, both subdomains, # attempt to submit the form without two hosts, both subdomains,
@ -1606,11 +1508,7 @@ class TestDomainNameservers(TestDomainOverview):
# the required field. nameserver has ip but missing host # the required field. nameserver has ip but missing host
self.assertContains( self.assertContains(
result, result,
str( str(NameserverError(code=NameserverErrorCodes.INVALID_IP, nameserver=nameserver)),
NameserverError(
code=NameserverErrorCodes.INVALID_IP, nameserver=nameserver
)
),
count=2, count=2,
status_code=200, status_code=200,
) )
@ -1623,9 +1521,7 @@ class TestDomainNameservers(TestDomainOverview):
nameserver = "invalid-nameserver.gov" nameserver = "invalid-nameserver.gov"
valid_ip = "123.2.45.111" valid_ip = "123.2.45.111"
# initial nameservers page has one server with two ips # initial nameservers page has one server with two ips
nameservers_page = self.app.get( nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# attempt to submit the form without two hosts, both subdomains, # attempt to submit the form without two hosts, both subdomains,
@ -1639,11 +1535,7 @@ class TestDomainNameservers(TestDomainOverview):
# the required field. nameserver has invalid host # the required field. nameserver has invalid host
self.assertContains( self.assertContains(
result, result,
str( str(NameserverError(code=NameserverErrorCodes.INVALID_HOST, nameserver=nameserver)),
NameserverError(
code=NameserverErrorCodes.INVALID_HOST, nameserver=nameserver
)
),
count=2, count=2,
status_code=200, status_code=200,
) )
@ -1657,9 +1549,7 @@ class TestDomainNameservers(TestDomainOverview):
nameserver2 = "ns2.igorville.gov" nameserver2 = "ns2.igorville.gov"
invalid_ip = "127.0.0.1" invalid_ip = "127.0.0.1"
# initial nameservers page has one server with two ips # initial nameservers page has one server with two ips
nameservers_page = self.app.get( nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# attempt to submit the form without two hosts, both subdomains, # attempt to submit the form without two hosts, both subdomains,
@ -1684,9 +1574,7 @@ class TestDomainNameservers(TestDomainOverview):
Uses self.app WebTest because we need to interact with forms. Uses self.app WebTest because we need to interact with forms.
""" """
nameservers_page = self.app.get( nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# first two nameservers are required, so if we empty one out we should # first two nameservers are required, so if we empty one out we should
@ -1708,9 +1596,7 @@ class TestDomainNameservers(TestDomainOverview):
class TestDomainAuthorizingOfficial(TestDomainOverview): class TestDomainAuthorizingOfficial(TestDomainOverview):
def test_domain_authorizing_official(self): def test_domain_authorizing_official(self):
"""Can load domain's authorizing official page.""" """Can load domain's authorizing official page."""
page = self.client.get( page = self.client.get(reverse("domain-authorizing-official", kwargs={"pk": self.domain.id}))
reverse("domain-authorizing-official", kwargs={"pk": self.domain.id})
)
# once on the sidebar, once in the title # once on the sidebar, once in the title
self.assertContains(page, "Authorizing official", count=2) self.assertContains(page, "Authorizing official", count=2)
@ -1719,18 +1605,14 @@ class TestDomainAuthorizingOfficial(TestDomainOverview):
self.domain_information.authorizing_official = Contact(first_name="Testy") self.domain_information.authorizing_official = Contact(first_name="Testy")
self.domain_information.authorizing_official.save() self.domain_information.authorizing_official.save()
self.domain_information.save() self.domain_information.save()
page = self.app.get( page = self.app.get(reverse("domain-authorizing-official", kwargs={"pk": self.domain.id}))
reverse("domain-authorizing-official", kwargs={"pk": self.domain.id})
)
self.assertContains(page, "Testy") self.assertContains(page, "Testy")
class TestDomainOrganization(TestDomainOverview): class TestDomainOrganization(TestDomainOverview):
def test_domain_org_name_address(self): def test_domain_org_name_address(self):
"""Can load domain's org name and mailing address page.""" """Can load domain's org name and mailing address page."""
page = self.client.get( page = self.client.get(reverse("domain-org-name-address", kwargs={"pk": self.domain.id}))
reverse("domain-org-name-address", kwargs={"pk": self.domain.id})
)
# once on the sidebar, once in the page title, once as H1 # once on the sidebar, once in the page title, once as H1
self.assertContains(page, "Organization name and mailing address", count=3) self.assertContains(page, "Organization name and mailing address", count=3)
@ -1738,18 +1620,14 @@ class TestDomainOrganization(TestDomainOverview):
"""Org name and address information appears on the page.""" """Org name and address information appears on the page."""
self.domain_information.organization_name = "Town of Igorville" self.domain_information.organization_name = "Town of Igorville"
self.domain_information.save() self.domain_information.save()
page = self.app.get( page = self.app.get(reverse("domain-org-name-address", kwargs={"pk": self.domain.id}))
reverse("domain-org-name-address", kwargs={"pk": self.domain.id})
)
self.assertContains(page, "Town of Igorville") self.assertContains(page, "Town of Igorville")
def test_domain_org_name_address_form(self): def test_domain_org_name_address_form(self):
"""Submitting changes works on the org name address page.""" """Submitting changes works on the org name address page."""
self.domain_information.organization_name = "Town of Igorville" self.domain_information.organization_name = "Town of Igorville"
self.domain_information.save() self.domain_information.save()
org_name_page = self.app.get( org_name_page = self.app.get(reverse("domain-org-name-address", kwargs={"pk": self.domain.id}))
reverse("domain-org-name-address", kwargs={"pk": self.domain.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
org_name_page.form["organization_name"] = "Not igorville" org_name_page.form["organization_name"] = "Not igorville"
@ -1766,18 +1644,14 @@ class TestDomainOrganization(TestDomainOverview):
class TestDomainContactInformation(TestDomainOverview): class TestDomainContactInformation(TestDomainOverview):
def test_domain_your_contact_information(self): def test_domain_your_contact_information(self):
"""Can load domain's your contact information page.""" """Can load domain's your contact information page."""
page = self.client.get( page = self.client.get(reverse("domain-your-contact-information", kwargs={"pk": self.domain.id}))
reverse("domain-your-contact-information", kwargs={"pk": self.domain.id})
)
self.assertContains(page, "Your contact information") self.assertContains(page, "Your contact information")
def test_domain_your_contact_information_content(self): def test_domain_your_contact_information_content(self):
"""Logged-in user's contact information appears on the page.""" """Logged-in user's contact information appears on the page."""
self.user.contact.first_name = "Testy" self.user.contact.first_name = "Testy"
self.user.contact.save() self.user.contact.save()
page = self.app.get( page = self.app.get(reverse("domain-your-contact-information", kwargs={"pk": self.domain.id}))
reverse("domain-your-contact-information", kwargs={"pk": self.domain.id})
)
self.assertContains(page, "Testy") self.assertContains(page, "Testy")
@ -1791,9 +1665,7 @@ class TestDomainSecurityEmail(TestDomainOverview):
domain_contact, _ = Domain.objects.get_or_create(name="freeman.gov") domain_contact, _ = Domain.objects.get_or_create(name="freeman.gov")
# Add current user to this domain # Add current user to this domain
_ = UserDomainRole(user=self.user, domain=domain_contact, role="admin").save() _ = UserDomainRole(user=self.user, domain=domain_contact, role="admin").save()
page = self.client.get( page = self.client.get(reverse("domain-security-email", kwargs={"pk": domain_contact.id}))
reverse("domain-security-email", kwargs={"pk": domain_contact.id})
)
# Loads correctly # Loads correctly
self.assertContains(page, "Security email") self.assertContains(page, "Security email")
@ -1807,9 +1679,7 @@ class TestDomainSecurityEmail(TestDomainOverview):
self.mockedSendFunction = self.mockSendPatch.start() self.mockedSendFunction = self.mockSendPatch.start()
self.mockedSendFunction.side_effect = self.mockSend self.mockedSendFunction.side_effect = self.mockSend
page = self.client.get( page = self.client.get(reverse("domain-security-email", kwargs={"pk": self.domain.id}))
reverse("domain-security-email", kwargs={"pk": self.domain.id})
)
# Loads correctly # Loads correctly
self.assertContains(page, "Security email") self.assertContains(page, "Security email")
@ -1818,18 +1688,14 @@ class TestDomainSecurityEmail(TestDomainOverview):
def test_domain_security_email(self): def test_domain_security_email(self):
"""Can load domain's security email page.""" """Can load domain's security email page."""
page = self.client.get( page = self.client.get(reverse("domain-security-email", kwargs={"pk": self.domain.id}))
reverse("domain-security-email", kwargs={"pk": self.domain.id})
)
self.assertContains(page, "Security email") self.assertContains(page, "Security email")
def test_domain_security_email_form(self): def test_domain_security_email_form(self):
"""Adding a security email works. """Adding a security email works.
Uses self.app WebTest because we need to interact with forms. Uses self.app WebTest because we need to interact with forms.
""" """
security_email_page = self.app.get( security_email_page = self.app.get(reverse("domain-security-email", kwargs={"pk": self.domain.id}))
reverse("domain-security-email", kwargs={"pk": self.domain.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
security_email_page.form["security_email"] = "mayor@igorville.gov" security_email_page.form["security_email"] = "mayor@igorville.gov"
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1843,9 +1709,7 @@ class TestDomainSecurityEmail(TestDomainOverview):
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
success_page = result.follow() success_page = result.follow()
self.assertContains( self.assertContains(success_page, "The security email for this domain has been updated")
success_page, "The security email for this domain has been updated"
)
def test_security_email_form_messages(self): def test_security_email_form_messages(self):
""" """
@ -1932,18 +1796,14 @@ class TestDomainDNSSEC(TestDomainOverview):
"""DNSSEC overview page loads when domain has no DNSSEC data """DNSSEC overview page loads when domain has no DNSSEC data
and shows a 'Enable DNSSEC' button.""" and shows a 'Enable DNSSEC' button."""
page = self.client.get( page = self.client.get(reverse("domain-dns-dnssec", kwargs={"pk": self.domain.id}))
reverse("domain-dns-dnssec", kwargs={"pk": self.domain.id})
)
self.assertContains(page, "Enable DNSSEC") self.assertContains(page, "Enable DNSSEC")
def test_dnssec_page_loads_with_data_in_domain(self): def test_dnssec_page_loads_with_data_in_domain(self):
"""DNSSEC overview page loads when domain has DNSSEC data """DNSSEC overview page loads when domain has DNSSEC data
and the template contains a button to disable DNSSEC.""" and the template contains a button to disable DNSSEC."""
page = self.client.get( page = self.client.get(reverse("domain-dns-dnssec", kwargs={"pk": self.domain_multdsdata.id}))
reverse("domain-dns-dnssec", kwargs={"pk": self.domain_multdsdata.id})
)
self.assertContains(page, "Disable DNSSEC") self.assertContains(page, "Disable DNSSEC")
# Prepare the data for the POST request # Prepare the data for the POST request
@ -1964,11 +1824,7 @@ class TestDomainDNSSEC(TestDomainOverview):
"""DNSSEC Add DS Data page loads when there is no """DNSSEC Add DS Data page loads when there is no
domain DNSSEC data and shows a button to Add new record""" domain DNSSEC data and shows a button to Add new record"""
page = self.client.get( page = self.client.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dnssec_none.id}))
reverse(
"domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dnssec_none.id}
)
)
self.assertContains(page, "You have no DS Data added") self.assertContains(page, "You have no DS Data added")
self.assertContains(page, "Add new record") self.assertContains(page, "Add new record")
@ -1976,16 +1832,12 @@ class TestDomainDNSSEC(TestDomainOverview):
"""DNSSEC Add DS Data page loads when there is """DNSSEC Add DS Data page loads when there is
domain DNSSEC DS data and shows the data""" domain DNSSEC DS data and shows the data"""
page = self.client.get( page = self.client.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}))
reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id})
)
self.assertContains(page, "DS Data record 1") self.assertContains(page, "DS Data record 1")
def test_ds_data_form_modal(self): def test_ds_data_form_modal(self):
"""When user clicks on save, a modal pops up.""" """When user clicks on save, a modal pops up."""
add_data_page = self.app.get( add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}))
reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id})
)
# Assert that a hidden trigger for the modal does not exist. # Assert that a hidden trigger for the modal does not exist.
# This hidden trigger will pop on the page when certain condition are met: # This hidden trigger will pop on the page when certain condition are met:
# 1) Initial form contained DS data, 2) All data is deleted and form is # 1) Initial form contained DS data, 2) All data is deleted and form is
@ -2006,9 +1858,7 @@ class TestDomainDNSSEC(TestDomainOverview):
Uses self.app WebTest because we need to interact with forms. Uses self.app WebTest because we need to interact with forms.
""" """
add_data_page = self.app.get( add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}))
reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
with less_console_noise(): # swallow log warning message with less_console_noise(): # swallow log warning message
@ -2021,18 +1871,14 @@ class TestDomainDNSSEC(TestDomainOverview):
) )
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
page = result.follow() page = result.follow()
self.assertContains( self.assertContains(page, "The DS Data records for this domain have been updated.")
page, "The DS Data records for this domain have been updated."
)
def test_ds_data_form_invalid(self): def test_ds_data_form_invalid(self):
"""DS Data form errors with invalid data """DS Data form errors with invalid data
Uses self.app WebTest because we need to interact with forms. Uses self.app WebTest because we need to interact with forms.
""" """
add_data_page = self.app.get( add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}))
reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# first two nameservers are required, so if we empty one out we should # first two nameservers are required, so if we empty one out we should
@ -2054,9 +1900,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
def test_application_status(self): def test_application_status(self):
"""Checking application status page""" """Checking application status page"""
application = completed_application( application = completed_application(status=DomainApplication.SUBMITTED, user=self.user)
status=DomainApplication.SUBMITTED, user=self.user
)
application.save() application.save()
home_page = self.app.get("/") home_page = self.app.get("/")
@ -2076,9 +1920,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
self.user.status = "ineligible" self.user.status = "ineligible"
self.user.save() self.user.save()
application = completed_application( application = completed_application(status=DomainApplication.SUBMITTED, user=self.user)
status=DomainApplication.SUBMITTED, user=self.user
)
application.save() application.save()
home_page = self.app.get("/") home_page = self.app.get("/")
@ -2093,9 +1935,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
def test_application_withdraw(self): def test_application_withdraw(self):
"""Checking application status page""" """Checking application status page"""
application = completed_application( application = completed_application(status=DomainApplication.SUBMITTED, user=self.user)
status=DomainApplication.SUBMITTED, user=self.user
)
application.save() application.save()
home_page = self.app.get("/") home_page = self.app.get("/")
@ -2125,9 +1965,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
def test_application_status_no_permissions(self): def test_application_status_no_permissions(self):
"""Can't access applications without being the creator.""" """Can't access applications without being the creator."""
application = completed_application( application = completed_application(status=DomainApplication.SUBMITTED, user=self.user)
status=DomainApplication.SUBMITTED, user=self.user
)
other_user = User() other_user = User()
other_user.save() other_user.save()
application.creator = other_user application.creator = other_user
@ -2141,17 +1979,13 @@ class TestApplicationStatus(TestWithUser, WebTest):
"application-withdrawn", "application-withdrawn",
]: ]:
with self.subTest(url_name=url_name): with self.subTest(url_name=url_name):
page = self.client.get( page = self.client.get(reverse(url_name, kwargs={"pk": application.pk}))
reverse(url_name, kwargs={"pk": application.pk})
)
self.assertEqual(page.status_code, 403) self.assertEqual(page.status_code, 403)
def test_approved_application_not_in_active_requests(self): def test_approved_application_not_in_active_requests(self):
"""An approved application is not shown in the Active """An approved application is not shown in the Active
Requests table on home.html.""" Requests table on home.html."""
application = completed_application( application = completed_application(status=DomainApplication.APPROVED, user=self.user)
status=DomainApplication.APPROVED, user=self.user
)
application.save() application.save()
home_page = self.app.get("/") home_page = self.app.get("/")

View file

@ -10,37 +10,25 @@ def export_domains_to_writer(writer, columns, sort_fields, filter_condition):
# write columns headers to writer # write columns headers to writer
writer.writerow(columns) writer.writerow(columns)
domainInfos = DomainInformation.objects.filter(**filter_condition).order_by( domainInfos = DomainInformation.objects.filter(**filter_condition).order_by(*sort_fields)
*sort_fields
)
for domainInfo in domainInfos: for domainInfo in domainInfos:
security_contacts = domainInfo.domain.contacts.filter( security_contacts = domainInfo.domain.contacts.filter(contact_type=PublicContact.ContactTypeChoices.SECURITY)
contact_type=PublicContact.ContactTypeChoices.SECURITY
)
# create a dictionary of fields which can be included in output # create a dictionary of fields which can be included in output
FIELDS = { FIELDS = {
"Domain name": domainInfo.domain.name, "Domain name": domainInfo.domain.name,
"Domain type": domainInfo.get_organization_type_display() "Domain type": domainInfo.get_organization_type_display() + " - " + domainInfo.get_federal_type_display()
+ " - "
+ domainInfo.get_federal_type_display()
if domainInfo.federal_type if domainInfo.federal_type
else domainInfo.get_organization_type_display(), else domainInfo.get_organization_type_display(),
"Agency": domainInfo.federal_agency, "Agency": domainInfo.federal_agency,
"Organization name": domainInfo.organization_name, "Organization name": domainInfo.organization_name,
"City": domainInfo.city, "City": domainInfo.city,
"State": domainInfo.state_territory, "State": domainInfo.state_territory,
"AO": domainInfo.authorizing_official.first_name "AO": domainInfo.authorizing_official.first_name + " " + domainInfo.authorizing_official.last_name
+ " "
+ domainInfo.authorizing_official.last_name
if domainInfo.authorizing_official if domainInfo.authorizing_official
else " ", else " ",
"AO email": domainInfo.authorizing_official.email "AO email": domainInfo.authorizing_official.email if domainInfo.authorizing_official else " ",
if domainInfo.authorizing_official "Security Contact Email": security_contacts[0].email if security_contacts else " ",
else " ",
"Security Contact Email": security_contacts[0].email
if security_contacts
else " ",
"Status": domainInfo.domain.state, "Status": domainInfo.domain.state,
"Expiration Date": domainInfo.domain.expiration_date, "Expiration Date": domainInfo.domain.expiration_date,
} }

View file

@ -13,9 +13,7 @@ class EmailSendingError(RuntimeError):
pass pass
def send_templated_email( def send_templated_email(template_name: str, subject_template_name: str, to_address: str, context={}):
template_name: str, subject_template_name: str, to_address: str, context={}
):
"""Send an email built from a template to one email address. """Send an email built from a template to one email address.
template_name and subject_template_name are relative to the same template template_name and subject_template_name are relative to the same template

View file

@ -87,28 +87,15 @@ class NameserverError(Exception):
""" """
_error_mapping = { _error_mapping = {
NameserverErrorCodes.MISSING_IP: ( NameserverErrorCodes.MISSING_IP: ("Using your domain for a name server requires an IP address"),
"Using your domain for a name server requires an IP address" NameserverErrorCodes.GLUE_RECORD_NOT_ALLOWED: ("Name server address does not match domain name"),
), NameserverErrorCodes.INVALID_IP: ("{}: Enter an IP address in the required format."),
NameserverErrorCodes.GLUE_RECORD_NOT_ALLOWED: ( NameserverErrorCodes.TOO_MANY_HOSTS: ("Too many hosts provided, you may not have more than 13 nameservers."),
"Name server address does not match domain name"
),
NameserverErrorCodes.INVALID_IP: (
"{}: Enter an IP address in the required format."
),
NameserverErrorCodes.TOO_MANY_HOSTS: (
"Too many hosts provided, you may not have more than 13 nameservers."
),
NameserverErrorCodes.UNABLE_TO_UPDATE_DOMAIN: ( NameserverErrorCodes.UNABLE_TO_UPDATE_DOMAIN: (
"Unable to update domain, changes were not applied." "Unable to update domain, changes were not applied. Check logs as a Registry Error is the likely cause"
"Check logs as a Registry Error is the likely cause"
),
NameserverErrorCodes.MISSING_HOST: (
"Name server must be provided to enter IP address."
),
NameserverErrorCodes.INVALID_HOST: (
"Enter a name server in the required format, like ns1.example.com"
), ),
NameserverErrorCodes.MISSING_HOST: ("Name server must be provided to enter IP address."),
NameserverErrorCodes.INVALID_HOST: ("Enter a name server in the required format, like ns1.example.com"),
} }
def __init__(self, *args, code=None, nameserver=None, ip=None, **kwargs): def __init__(self, *args, code=None, nameserver=None, ip=None, **kwargs):

View file

@ -93,19 +93,11 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
# We can use a dictionary with step names and callables that return booleans # We can use a dictionary with step names and callables that return booleans
# to show or hide particular steps based on the state of the process. # to show or hide particular steps based on the state of the process.
WIZARD_CONDITIONS = { WIZARD_CONDITIONS = {
Step.ORGANIZATION_FEDERAL: lambda w: w.from_model( Step.ORGANIZATION_FEDERAL: lambda w: w.from_model("show_organization_federal", False),
"show_organization_federal", False
),
Step.TRIBAL_GOVERNMENT: lambda w: w.from_model("show_tribal_government", False), Step.TRIBAL_GOVERNMENT: lambda w: w.from_model("show_tribal_government", False),
Step.ORGANIZATION_ELECTION: lambda w: w.from_model( Step.ORGANIZATION_ELECTION: lambda w: w.from_model("show_organization_election", False),
"show_organization_election", False Step.ABOUT_YOUR_ORGANIZATION: lambda w: w.from_model("show_about_your_organization", False),
), Step.NO_OTHER_CONTACTS: lambda w: w.from_model("show_no_other_contacts_rationale", False),
Step.ABOUT_YOUR_ORGANIZATION: lambda w: w.from_model(
"show_about_your_organization", False
),
Step.NO_OTHER_CONTACTS: lambda w: w.from_model(
"show_no_other_contacts_rationale", False
),
} }
def __init__(self): def __init__(self):

View file

@ -170,9 +170,7 @@ class DomainOrgNameAddressView(DomainFormBaseView):
"""The form is valid, save the organization name and mailing address.""" """The form is valid, save the organization name and mailing address."""
form.save() form.save()
messages.success( messages.success(self.request, "The organization name and mailing address has been updated.")
self.request, "The organization name and mailing address has been updated."
)
# superclass has the redirect # superclass has the redirect
return super().form_valid(form) return super().form_valid(form)
@ -200,9 +198,7 @@ class DomainAuthorizingOfficialView(DomainFormBaseView):
"""The form is valid, save the authorizing official.""" """The form is valid, save the authorizing official."""
form.save() form.save()
messages.success( messages.success(self.request, "The authorizing official for this domain has been updated.")
self.request, "The authorizing official for this domain has been updated."
)
# superclass has the redirect # superclass has the redirect
return super().form_valid(form) return super().form_valid(form)
@ -228,9 +224,7 @@ class DomainNameserversView(DomainFormBaseView):
if nameservers is not None: if nameservers is not None:
# Add existing nameservers as initial data # Add existing nameservers as initial data
initial_data.extend( initial_data.extend({"server": name, "ip": ",".join(ip)} for name, ip in nameservers)
{"server": name, "ip": ",".join(ip)} for name, ip in nameservers
)
# Ensure at least 3 fields, filled or empty # Ensure at least 3 fields, filled or empty
while len(initial_data) < 2: while len(initial_data) < 2:
@ -310,9 +304,7 @@ class DomainNameserversView(DomainFormBaseView):
except NameserverError as Err: except NameserverError as Err:
# NamserverErrors *should* be caught in form; if reached here, # NamserverErrors *should* be caught in form; if reached here,
# there was an uncaught error in submission (through EPP) # there was an uncaught error in submission (through EPP)
messages.error( messages.error(self.request, NameserverError(code=nsErrorCodes.UNABLE_TO_UPDATE_DOMAIN))
self.request, NameserverError(code=nsErrorCodes.UNABLE_TO_UPDATE_DOMAIN)
)
logger.error(f"Nameservers error: {Err}") logger.error(f"Nameservers error: {Err}")
# TODO: registry is not throwing an error when no connection # TODO: registry is not throwing an error when no connection
except RegistryError as Err: except RegistryError as Err:
@ -323,9 +315,7 @@ class DomainNameserversView(DomainFormBaseView):
) )
logger.error(f"Registry connection error: {Err}") logger.error(f"Registry connection error: {Err}")
else: else:
messages.error( messages.error(self.request, GenericError(code=GenericErrorCodes.GENERIC_ERROR))
self.request, GenericError(code=GenericErrorCodes.GENERIC_ERROR)
)
logger.error(f"Registry error: {Err}") logger.error(f"Registry error: {Err}")
else: else:
messages.success( messages.success(
@ -501,15 +491,11 @@ class DomainDsDataView(DomainFormBaseView):
) )
logger.error(f"Registry connection error: {err}") logger.error(f"Registry connection error: {err}")
else: else:
messages.error( messages.error(self.request, GenericError(code=GenericErrorCodes.GENERIC_ERROR))
self.request, GenericError(code=GenericErrorCodes.GENERIC_ERROR)
)
logger.error(f"Registry error: {err}") logger.error(f"Registry error: {err}")
return self.form_invalid(formset) return self.form_invalid(formset)
else: else:
messages.success( messages.success(self.request, "The DS Data records for this domain have been updated.")
self.request, "The DS Data records for this domain have been updated."
)
# superclass has the redirect # superclass has the redirect
return super().form_valid(formset) return super().form_valid(formset)
@ -536,9 +522,7 @@ class DomainYourContactInformationView(DomainFormBaseView):
# Post to DB using values from the form # Post to DB using values from the form
form.save() form.save()
messages.success( messages.success(self.request, "Your contact information for this domain has been updated.")
self.request, "Your contact information for this domain has been updated."
)
# superclass has the redirect # superclass has the redirect
return super().form_valid(form) return super().form_valid(form)
@ -597,19 +581,13 @@ class DomainSecurityEmailView(DomainFormBaseView):
) )
logger.error(f"Registry connection error: {Err}") logger.error(f"Registry connection error: {Err}")
else: else:
messages.error( messages.error(self.request, GenericError(code=GenericErrorCodes.GENERIC_ERROR))
self.request, GenericError(code=GenericErrorCodes.GENERIC_ERROR)
)
logger.error(f"Registry error: {Err}") logger.error(f"Registry error: {Err}")
except ContactError as Err: except ContactError as Err:
messages.error( messages.error(self.request, GenericError(code=GenericErrorCodes.GENERIC_ERROR))
self.request, GenericError(code=GenericErrorCodes.GENERIC_ERROR)
)
logger.error(f"Generic registry error: {Err}") logger.error(f"Generic registry error: {Err}")
else: else:
messages.success( messages.success(self.request, "The security email for this domain has been updated.")
self.request, "The security email for this domain has been updated."
)
# superclass has the redirect # superclass has the redirect
return redirect(self.get_success_url()) return redirect(self.get_success_url())
@ -636,15 +614,11 @@ class DomainAddUserView(DomainFormBaseView):
def _domain_abs_url(self): def _domain_abs_url(self):
"""Get an absolute URL for this domain.""" """Get an absolute URL for this domain."""
return self.request.build_absolute_uri( return self.request.build_absolute_uri(reverse("domain", kwargs={"pk": self.object.id}))
reverse("domain", kwargs={"pk": self.object.id})
)
def _make_invitation(self, email_address): def _make_invitation(self, email_address):
"""Make a Domain invitation for this email and redirect with a message.""" """Make a Domain invitation for this email and redirect with a message."""
invitation, created = DomainInvitation.objects.get_or_create( invitation, created = DomainInvitation.objects.get_or_create(email=email_address, domain=self.object)
email=email_address, domain=self.object
)
if not created: if not created:
# that invitation already existed # that invitation already existed
messages.warning( messages.warning(
@ -678,9 +652,7 @@ class DomainAddUserView(DomainFormBaseView):
exc_info=True, exc_info=True,
) )
else: else:
messages.success( messages.success(self.request, f"Invited {email_address} to this domain.")
self.request, f"Invited {email_address} to this domain."
)
return redirect(self.get_success_url()) return redirect(self.get_success_url())
@ -709,9 +681,7 @@ class DomainAddUserView(DomainFormBaseView):
return redirect(self.get_success_url()) return redirect(self.get_success_url())
class DomainInvitationDeleteView( class DomainInvitationDeleteView(DomainInvitationPermissionDeleteView, SuccessMessageMixin):
DomainInvitationPermissionDeleteView, SuccessMessageMixin
):
object: DomainInvitation # workaround for type mismatch in DeleteView object: DomainInvitation # workaround for type mismatch in DeleteView
def get_success_url(self): def get_success_url(self):

View file

@ -7,6 +7,4 @@ from login_required import login_not_required
# PaaS orchestrator can make sure the app has come up properly # PaaS orchestrator can make sure the app has come up properly
@login_not_required @login_not_required
def health(request): def health(request):
return HttpResponse( return HttpResponse('<html lang="en"><head><title>OK - Get.gov</title></head><body>OK</body>')
'<html lang="en"><head><title>OK - Get.gov</title></head><body>OK</body>'
)

View file

@ -2,7 +2,6 @@ from django.db.models import F
from django.shortcuts import render from django.shortcuts import render
from registrar.models import DomainApplication from registrar.models import DomainApplication
from django.conf import settings
def index(request): def index(request):
@ -23,5 +22,4 @@ def index(request):
state=F("domain__state"), state=F("domain__state"),
) )
context["domains"] = domains context["domains"] = domains
context["is_production"] = settings.IS_PRODUCTION
return render(request, "home.html", context) return render(request, "home.html", context)

View file

@ -49,9 +49,7 @@ class DomainPermission(PermissionsLoginMixin):
return True return True
# user needs to have a role on the domain # user needs to have a role on the domain
if not UserDomainRole.objects.filter( if not UserDomainRole.objects.filter(user=self.request.user, domain__id=pk).exists():
user=self.request.user, domain__id=pk
).exists():
return False return False
# if we need to check more about the nature of role, do it here. # if we need to check more about the nature of role, do it here.
@ -133,9 +131,7 @@ class DomainApplicationPermission(PermissionsLoginMixin):
# user needs to be the creator of the application # user needs to be the creator of the application
# this query is empty if there isn't a domain application with this # this query is empty if there isn't a domain application with this
# id and this user as creator # id and this user as creator
if not DomainApplication.objects.filter( if not DomainApplication.objects.filter(creator=self.request.user, id=self.kwargs["pk"]).exists():
creator=self.request.user, id=self.kwargs["pk"]
).exists():
return False return False
return True return True

View file

@ -33,9 +33,9 @@ class DomainPermissionView(DomainPermission, DetailView, abc.ABC):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
user = self.request.user user = self.request.user
context["is_analyst_or_superuser"] = user.has_perm( context["is_analyst_or_superuser"] = user.has_perm("registrar.analyst_access_permission") or user.has_perm(
"registrar.analyst_access_permission" "registrar.full_access_permission"
) or user.has_perm("registrar.full_access_permission") )
# Stored in a variable for the linter # Stored in a variable for the linter
action = "analyst_action" action = "analyst_action"
action_location = "analyst_action_location" action_location = "analyst_action_location"
@ -74,9 +74,7 @@ class DomainApplicationPermissionView(DomainApplicationPermission, DetailView, a
raise NotImplementedError raise NotImplementedError
class ApplicationWizardPermissionView( class ApplicationWizardPermissionView(ApplicationWizardPermission, TemplateView, abc.ABC):
ApplicationWizardPermission, TemplateView, abc.ABC
):
"""Abstract base view for the application form that enforces permissions """Abstract base view for the application form that enforces permissions
@ -91,9 +89,7 @@ class ApplicationWizardPermissionView(
raise NotImplementedError raise NotImplementedError
class DomainInvitationPermissionDeleteView( class DomainInvitationPermissionDeleteView(DomainInvitationPermission, DeleteView, abc.ABC):
DomainInvitationPermission, DeleteView, abc.ABC
):
"""Abstract view for deleting a domain invitation. """Abstract view for deleting a domain invitation.