Merge branch 'main' into rjm/2906-adr-ajax

This commit is contained in:
Rachid Mrad 2025-02-20 11:19:09 -05:00 committed by GitHub
commit b589a1a806
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
59 changed files with 1540 additions and 1628 deletions

View file

@ -5695,19 +5695,35 @@ const createHeaderButton = (header, headerName) => {
buttonEl.setAttribute("tabindex", "0");
buttonEl.classList.add(SORT_BUTTON_CLASS);
// ICON_SOURCE
// ---- END DOTGOV EDIT
// Change icons on sort, use source from arro_upward and arrow_downward
// buttonEl.innerHTML = Sanitizer.escapeHTML`
// <svg class="${PREFIX}-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
// <g class="descending" fill="transparent">
// <path d="M17 17L15.59 15.59L12.9999 18.17V2H10.9999V18.17L8.41 15.58L7 17L11.9999 22L17 17Z" />
// </g>
// <g class="ascending" fill="transparent">
// <path transform="rotate(180, 12, 12)" d="M17 17L15.59 15.59L12.9999 18.17V2H10.9999V18.17L8.41 15.58L7 17L11.9999 22L17 17Z" />
// </g>
// <g class="unsorted" fill="transparent">
// <polygon points="15.17 15 13 17.17 13 6.83 15.17 9 16.58 7.59 12 3 7.41 7.59 8.83 9 11 6.83 11 17.17 8.83 15 7.42 16.41 12 21 16.59 16.41 15.17 15"/>
// </g>
// </svg>
// `;
buttonEl.innerHTML = Sanitizer.escapeHTML`
<svg class="${PREFIX}-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g class="descending" fill="transparent">
<path d="M17 17L15.59 15.59L12.9999 18.17V2H10.9999V18.17L8.41 15.58L7 17L11.9999 22L17 17Z" />
<path d="m20 12-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"/>
</g>
<g class="ascending" fill="transparent">
<path transform="rotate(180, 12, 12)" d="M17 17L15.59 15.59L12.9999 18.17V2H10.9999V18.17L8.41 15.58L7 17L11.9999 22L17 17Z" />
<path d="m4 12 1.41 1.41L11 7.83V20h2V7.83l5.58 5.59L20 12l-8-8-8 8z"/>
</g>
<g class="unsorted" fill="transparent">
<polygon points="15.17 15 13 17.17 13 6.83 15.17 9 16.58 7.59 12 3 7.41 7.59 8.83 9 11 6.83 11 17.17 8.83 15 7.42 16.41 12 21 16.59 16.41 15.17 15"/>
</g>
</svg>
`;
// ---- END DOTGOV EDIT
header.appendChild(buttonEl);
updateSortLabel(header, headerName);
};

View file

@ -2,11 +2,41 @@ import { submitForm } from './helpers.js';
export function initDomainRequestForm() {
document.addEventListener('DOMContentLoaded', function() {
const button = document.getElementById("domain-request-form-submit-button");
if (button) {
button.addEventListener("click", function () {
submitForm("submit-domain-request-form");
});
}
// These are the request steps in DomainRequestWizard, such as current_websites or review
initRequestStepCurrentWebsitesListener();
initRequestStepReviewListener();
});
}
function initRequestStepReviewListener() {
const button = document.getElementById("domain-request-form-submit-button");
if (button) {
button.addEventListener("click", function () {
submitForm("submit-domain-request-form");
});
}
}
function initRequestStepCurrentWebsitesListener() {
//register-form-step
const addAnotherSiteButton = document.getElementById("submit-domain-request--site-button");
if (addAnotherSiteButton) {
// Check for focus state in sessionStorage
const focusTarget = sessionStorage.getItem("lastFocusedElement");
if (focusTarget) {
document.querySelector(focusTarget)?.focus();
}
// Add form submit handler to store focus state
const form = document.querySelector("form");
if (form) {
form.addEventListener("submit", () => {
const activeElement = document.activeElement;
if (activeElement) {
sessionStorage.setItem("lastFocusedElement", "#" + activeElement.id);
}
});
}
// We only want to do this action once, so we clear out the session
sessionStorage.removeItem("lastFocusedElement");
}
}

View file

@ -520,15 +520,6 @@ input[type=submit].button--dja-toolbar:focus, input[type=submit].button--dja-too
}
}
.module--custom {
a {
font-size: 13px;
font-weight: 600;
border: solid 1px var(--darkened-bg);
background: var(--darkened-bg);
}
}
.usa-modal--django-admin .usa-prose ul > li {
list-style-type: inherit;
// Styling based off of the <p> styling in django admin
@ -839,6 +830,17 @@ div.dja__model-description{
text-transform: capitalize;
}
.module caption {
// Match the old <h2> size for django admin
font-size: 0.8125rem;
}
// text-bold doesn't work here due to style overrides, unfortunately.
// This is a workaround.
caption.text-bold {
font-weight: font-weight('bold');
}
.wrapped-button-group {
// This button group has too many items
flex-wrap: wrap;

View file

@ -41,13 +41,8 @@ th {
}
}
// The member table has an extra "expand" row, which looks like a single row.
// But the DOM disagrees - so we basically need to hide the border on both rows.
#members__table-wrapper .dotgov-table tr:nth-last-child(2) td,
#members__table-wrapper .dotgov-table tr:nth-last-child(2) th {
border-bottom: none;
}
// .dotgov-table allows us to customize .usa-table on the user-facing pages,
// while leaving the default styles for use on the admin pages
.dotgov-table {
width: 100%;
@ -68,7 +63,8 @@ th {
border-bottom: 1px solid color('base-lighter');
}
thead th {
thead th,
thead th[aria-sort] {
color: color('primary-darker');
border-bottom: 2px solid color('base-light');
}
@ -93,17 +89,46 @@ th {
}
}
@include at-media(tablet-lg) {
th[data-sortable] .usa-table__header__button {
right: auto;
&[aria-sort=ascending],
&[aria-sort=descending],
&:not([aria-sort]) {
right: auto;
// Sortable headers
th[data-sortable][aria-sort=ascending],
th[data-sortable][aria-sort=descending] {
background-color: transparent;
.usa-table__header__button {
background-color: color('accent-cool-lightest');
border-radius: units(.5);
color: color('primary-darker');
&:hover {
background-color: color('accent-cool-lightest');
}
}
}
@include at-media(tablet-lg) {
th[data-sortable]:not(.left-align-sort-button) .usa-table__header__button {
// position next to the copy
right: auto;
// slide left to mock a margin between the copy and the icon
transform: translateX(units(1));
// fix vertical alignment
top: units(1.5);
}
th[data-sortable].left-align-sort-button .usa-table__header__button {
left: 0;
}
}
// Currently the 'flash' when sort is clicked,
// this will become persistent if the double-sort bug is fixed
td[data-sort-active],
th[data-sort-active] {
background-color: color('primary-lightest');
}
}
// The member table has an extra "expand" row, which looks like a single row.
// But the DOM disagrees - so we basically need to hide the border on both rows.
#members__table-wrapper .dotgov-table tr:nth-last-child(2) td,
#members__table-wrapper .dotgov-table tr:nth-last-child(2) th {
border-bottom: none;
}
.dotgov-table--cell-padding-2 {

View file

@ -70,6 +70,7 @@ in the form $setting: value,
----------------------------*/
$theme-font-weight-medium: 400,
$theme-font-weight-semibold: 600,
$theme-font-weight-bold: 700,
/*---------------------------
## Font roles

View file

@ -201,6 +201,8 @@ MIDDLEWARE = [
"waffle.middleware.WaffleMiddleware",
"registrar.registrar_middleware.CheckUserProfileMiddleware",
"registrar.registrar_middleware.CheckPortfolioMiddleware",
# Restrict access using Opt-Out approach
"registrar.registrar_middleware.RestrictAccessMiddleware",
]
# application object used by Django's built-in servers (e.g. `runserver`)

View file

@ -68,7 +68,7 @@ for step, view in [
(PortfolioDomainRequestStep.REQUESTING_ENTITY, views.RequestingEntity),
(PortfolioDomainRequestStep.ADDITIONAL_DETAILS, views.PortfolioAdditionalDetails),
]:
domain_request_urls.append(path(f"<int:id>/{step}/", view.as_view(), name=step))
domain_request_urls.append(path(f"<int:domain_request_pk>/{step}/", view.as_view(), name=step))
urlpatterns = [
@ -260,27 +260,27 @@ urlpatterns = [
name="export_data_type_user",
),
path(
"domain-request/<int:id>/edit/",
"domain-request/<int:domain_request_pk>/edit/",
views.DomainRequestWizard.as_view(),
name=views.DomainRequestWizard.EDIT_URL_NAME,
),
path(
"domain-request/<int:pk>",
"domain-request/<int:domain_request_pk>",
views.DomainRequestStatus.as_view(),
name="domain-request-status",
),
path(
"domain-request/viewonly/<int:pk>",
"domain-request/viewonly/<int:domain_request_pk>",
views.PortfolioDomainRequestStatusViewOnly.as_view(),
name="domain-request-status-viewonly",
),
path(
"domain-request/<int:pk>/withdraw",
"domain-request/<int:domain_request_pk>/withdraw",
views.DomainRequestWithdrawConfirmation.as_view(),
name="domain-request-withdraw-confirmation",
),
path(
"domain-request/<int:pk>/withdrawconfirmed",
"domain-request/<int:domain_request_pk>/withdrawconfirmed",
views.DomainRequestWithdrawn.as_view(),
name="domain-request-withdrawn",
),
@ -296,56 +296,60 @@ urlpatterns = [
lambda r: always_404(r, "We forgot to include this link, sorry."),
name="todo",
),
path("domain/<int:pk>", views.DomainView.as_view(), name="domain"),
path("domain/<int:pk>/prototype-dns", views.PrototypeDomainDNSRecordView.as_view(), name="prototype-domain-dns"),
path("domain/<int:pk>/users", views.DomainUsersView.as_view(), name="domain-users"),
path("domain/<int:domain_pk>", views.DomainView.as_view(), name="domain"),
path(
"domain/<int:pk>/dns",
"domain/<int:domain_pk>/prototype-dns",
views.PrototypeDomainDNSRecordView.as_view(),
name="prototype-domain-dns",
),
path("domain/<int:domain_pk>/users", views.DomainUsersView.as_view(), name="domain-users"),
path(
"domain/<int:domain_pk>/dns",
views.DomainDNSView.as_view(),
name="domain-dns",
),
path(
"domain/<int:pk>/dns/nameservers",
"domain/<int:domain_pk>/dns/nameservers",
views.DomainNameserversView.as_view(),
name="domain-dns-nameservers",
),
path(
"domain/<int:pk>/dns/dnssec",
"domain/<int:domain_pk>/dns/dnssec",
views.DomainDNSSECView.as_view(),
name="domain-dns-dnssec",
),
path(
"domain/<int:pk>/dns/dnssec/dsdata",
"domain/<int:domain_pk>/dns/dnssec/dsdata",
views.DomainDsDataView.as_view(),
name="domain-dns-dnssec-dsdata",
),
path(
"domain/<int:pk>/org-name-address",
"domain/<int:domain_pk>/org-name-address",
views.DomainOrgNameAddressView.as_view(),
name="domain-org-name-address",
),
path(
"domain/<int:pk>/suborganization",
"domain/<int:domain_pk>/suborganization",
views.DomainSubOrganizationView.as_view(),
name="domain-suborganization",
),
path(
"domain/<int:pk>/senior-official",
"domain/<int:domain_pk>/senior-official",
views.DomainSeniorOfficialView.as_view(),
name="domain-senior-official",
),
path(
"domain/<int:pk>/security-email",
"domain/<int:domain_pk>/security-email",
views.DomainSecurityEmailView.as_view(),
name="domain-security-email",
),
path(
"domain/<int:pk>/renewal",
"domain/<int:domain_pk>/renewal",
views.DomainRenewalView.as_view(),
name="domain-renewal",
),
path(
"domain/<int:pk>/users/add",
"domain/<int:domain_pk>/users/add",
views.DomainAddUserView.as_view(),
name="domain-users-add",
),
@ -360,17 +364,17 @@ urlpatterns = [
name="user-profile",
),
path(
"invitation/<int:pk>/cancel",
"invitation/<int:domain_invitation_pk>/cancel",
views.DomainInvitationCancelView.as_view(http_method_names=["post"]),
name="invitation-cancel",
),
path(
"domain-request/<int:pk>/delete",
"domain-request/<int:domain_request_pk>/delete",
views.DomainRequestDeleteView.as_view(http_method_names=["post"]),
name="domain-request-delete",
),
path(
"domain/<int:pk>/users/<int:user_pk>/delete",
"domain/<int:domain_pk>/users/<int:user_pk>/delete",
views.DomainDeleteUserView.as_view(http_method_names=["post"]),
name="domain-user-delete",
),
@ -392,6 +396,7 @@ urlpatterns = [
# This way, we can share a view for djangooidc, and other pages as we see fit.
handler500 = "registrar.views.utility.error_views.custom_500_error_view"
handler403 = "registrar.views.utility.error_views.custom_403_error_view"
handler404 = "registrar.views.utility.error_views.custom_404_error_view"
# we normally would guard these with `if settings.DEBUG` but tests run with
# DEBUG = False even when these apps have been loaded because settings.DEBUG

300
src/registrar/decorators.py Normal file
View file

@ -0,0 +1,300 @@
import functools
from django.core.exceptions import PermissionDenied
from django.utils.decorators import method_decorator
from registrar.models import Domain, DomainInformation, DomainInvitation, DomainRequest, UserDomainRole
# Constants for clarity
ALL = "all"
IS_STAFF = "is_staff"
IS_DOMAIN_MANAGER = "is_domain_manager"
IS_DOMAIN_REQUEST_CREATOR = "is_domain_request_creator"
IS_STAFF_MANAGING_DOMAIN = "is_staff_managing_domain"
IS_PORTFOLIO_MEMBER = "is_portfolio_member"
IS_PORTFOLIO_MEMBER_AND_DOMAIN_MANAGER = "is_portfolio_member_and_domain_manager"
IS_DOMAIN_MANAGER_AND_NOT_PORTFOLIO_MEMBER = "is_domain_manager_and_not_portfolio_member"
HAS_PORTFOLIO_DOMAINS_ANY_PERM = "has_portfolio_domains_any_perm"
HAS_PORTFOLIO_DOMAINS_VIEW_ALL = "has_portfolio_domains_view_all"
HAS_PORTFOLIO_DOMAIN_REQUESTS_ANY_PERM = "has_portfolio_domain_requests_any_perm"
HAS_PORTFOLIO_DOMAIN_REQUESTS_VIEW_ALL = "has_portfolio_domain_requests_view_all"
HAS_PORTFOLIO_DOMAIN_REQUESTS_EDIT = "has_portfolio_domain_requests_edit"
HAS_PORTFOLIO_MEMBERS_ANY_PERM = "has_portfolio_members_any_perm"
HAS_PORTFOLIO_MEMBERS_EDIT = "has_portfolio_members_edit"
HAS_PORTFOLIO_MEMBERS_VIEW = "has_portfolio_members_view"
def grant_access(*rules):
"""
A decorator that enforces access control based on specified rules.
Usage:
- Multiple rules in a single decorator:
@grant_access(IS_STAFF, IS_SUPERUSER, IS_DOMAIN_MANAGER)
- Stacked decorators for separate rules:
@grant_access(IS_SUPERUSER)
@grant_access(IS_DOMAIN_MANAGER)
The decorator supports both function-based views (FBVs) and class-based views (CBVs).
"""
def decorator(view):
if isinstance(view, type): # Check if decorating a class-based view (CBV)
original_dispatch = view.dispatch # Store the original dispatch method
@method_decorator(grant_access(*rules)) # Apply the decorator to dispatch
def wrapped_dispatch(self, request, *args, **kwargs):
if not _user_has_permission(request.user, request, rules, **kwargs):
raise PermissionDenied # Deny access if the user lacks permission
return original_dispatch(self, request, *args, **kwargs)
view.dispatch = wrapped_dispatch # Replace the dispatch method
return view
else: # If decorating a function-based view (FBV)
view.has_explicit_access = True # Mark the view as having explicit access control
existing_rules = getattr(view, "_access_rules", set()) # Retrieve existing rules
existing_rules.update(rules) # Merge with new rules
view._access_rules = existing_rules # Store updated rules
@functools.wraps(view)
def wrapper(request, *args, **kwargs):
if not _user_has_permission(request.user, request, rules, **kwargs):
raise PermissionDenied # Deny access if the user lacks permission
return view(request, *args, **kwargs) # Proceed with the original view
return wrapper
return decorator
def _user_has_permission(user, request, rules, **kwargs):
"""
Determines if the user meets the required permission rules.
This function evaluates a set of predefined permission rules to check whether a user has access
to a specific view. It supports various access control conditions, including staff status,
domain management roles, and portfolio-related permissions.
Parameters:
- user: The user requesting access.
- request: The HTTP request object.
- rules: A set of access control rules to evaluate.
- **kwargs: Additional keyword arguments used in specific permission checks.
Returns:
- True if the user satisfies any of the specified rules.
- False otherwise.
"""
# Skip authentication if @login_not_required is applied
if getattr(request, "login_not_required", False):
return True
# Allow everyone if `ALL` is in rules
if ALL in rules:
return True
# Ensure user is authenticated and not restricted
if not user.is_authenticated or user.is_restricted():
return False
# Define permission checks
permission_checks = [
(IS_STAFF, lambda: user.is_staff),
(IS_DOMAIN_MANAGER, lambda: _is_domain_manager(user, **kwargs)),
(IS_STAFF_MANAGING_DOMAIN, lambda: _is_staff_managing_domain(request, **kwargs)),
(IS_PORTFOLIO_MEMBER, lambda: user.is_org_user(request)),
(
HAS_PORTFOLIO_DOMAINS_VIEW_ALL,
lambda: _has_portfolio_view_all_domains(request, kwargs.get("domain_pk")),
),
(
HAS_PORTFOLIO_DOMAINS_ANY_PERM,
lambda: user.is_org_user(request)
and user.has_any_domains_portfolio_permission(request.session.get("portfolio")),
),
(
IS_PORTFOLIO_MEMBER_AND_DOMAIN_MANAGER,
lambda: _is_domain_manager(user, **kwargs) and _is_portfolio_member(request),
),
(
IS_DOMAIN_MANAGER_AND_NOT_PORTFOLIO_MEMBER,
lambda: _is_domain_manager(user, **kwargs) and not _is_portfolio_member(request),
),
(
IS_DOMAIN_REQUEST_CREATOR,
lambda: _is_domain_request_creator(user, kwargs.get("domain_request_pk"))
and not _is_portfolio_member(request),
),
(
HAS_PORTFOLIO_DOMAIN_REQUESTS_ANY_PERM,
lambda: user.is_org_user(request)
and user.has_any_requests_portfolio_permission(request.session.get("portfolio")),
),
(
HAS_PORTFOLIO_DOMAIN_REQUESTS_VIEW_ALL,
lambda: user.is_org_user(request)
and user.has_view_all_domain_requests_portfolio_permission(request.session.get("portfolio")),
),
(
HAS_PORTFOLIO_DOMAIN_REQUESTS_EDIT,
lambda: _has_portfolio_domain_requests_edit(user, request, kwargs.get("domain_request_pk")),
),
(
HAS_PORTFOLIO_MEMBERS_ANY_PERM,
lambda: user.is_org_user(request)
and (
user.has_view_members_portfolio_permission(request.session.get("portfolio"))
or user.has_edit_members_portfolio_permission(request.session.get("portfolio"))
),
),
(
HAS_PORTFOLIO_MEMBERS_EDIT,
lambda: user.is_org_user(request)
and user.has_edit_members_portfolio_permission(request.session.get("portfolio")),
),
(
HAS_PORTFOLIO_MEMBERS_VIEW,
lambda: user.is_org_user(request)
and user.has_view_members_portfolio_permission(request.session.get("portfolio")),
),
]
# Check conditions iteratively
return any(check() for rule, check in permission_checks if rule in rules)
def _has_portfolio_domain_requests_edit(user, request, domain_request_id):
if domain_request_id and not _is_domain_request_creator(user, domain_request_id):
return False
return user.is_org_user(request) and user.has_edit_request_portfolio_permission(request.session.get("portfolio"))
def _is_domain_manager(user, **kwargs):
"""
Determines if the given user is a domain manager for a specified domain.
- First, it checks if 'domain_pk' is present in the URL parameters.
- If 'domain_pk' exists, it verifies if the user has a domain role for that domain.
- If 'domain_pk' is absent, it checks for 'domain_invitation_pk' to determine if the user
has domain permissions through an invitation.
Returns:
bool: True if the user is a domain manager, False otherwise.
"""
domain_id = kwargs.get("domain_pk")
if domain_id:
return UserDomainRole.objects.filter(user=user, domain_id=domain_id).exists()
domain_invitation_id = kwargs.get("domain_invitation_pk")
if domain_invitation_id:
return DomainInvitation.objects.filter(id=domain_invitation_id, domain__permissions__user=user).exists()
return False
def _is_domain_request_creator(user, domain_request_pk):
"""Checks to see if the user is the creator of a domain request
with domain_request_pk."""
if domain_request_pk:
return DomainRequest.objects.filter(creator=user, id=domain_request_pk).exists()
return True
def _is_portfolio_member(request):
"""Checks to see if the user in the request is a member of the
portfolio in the request's session."""
return request.user.is_org_user(request)
def _is_staff_managing_domain(request, **kwargs):
"""
Determines whether a staff user (analyst or superuser) has permission to manage a domain
that they did not create or were not invited to.
The function enforces:
1. **User Authorization** - The user must have `analyst_access_permission` or `full_access_permission`.
2. **Valid Session Context** - The user must have explicitly selected the domain for management
via an 'analyst action' (e.g., by clicking 'Manage Domain' in the admin interface).
3. **Domain Status Check** - Only domains in specific statuses (e.g., APPROVED, IN_REVIEW, etc.)
can be managed, except in cases where the domain lacks a status due to errors.
Process:
- First, the function retrieves the `domain_pk` from the URL parameters.
- If `domain_pk` is not provided, it attempts to resolve the domain via `domain_invitation_pk`.
- It checks if the user has the required permissions.
- It verifies that the user has an active 'analyst action' session for the domain.
- Finally, it ensures that the domain is in a status that allows management.
Returns:
bool: True if the user is allowed to manage the domain, False otherwise.
"""
domain_id = kwargs.get("domain_pk")
if not domain_id:
domain_invitation_id = kwargs.get("domain_invitation_pk")
domain_invitation = DomainInvitation.objects.filter(id=domain_invitation_id).first()
if domain_invitation:
domain_id = domain_invitation.domain_id
# Check if the request user is permissioned...
user_is_analyst_or_superuser = request.user.has_perm(
"registrar.analyst_access_permission"
) or request.user.has_perm("registrar.full_access_permission")
if not user_is_analyst_or_superuser:
return False
# Check if the user is attempting a valid edit action.
# In other words, if the analyst/admin did not click
# the 'Manage Domain' button in /admin,
# then they cannot access this page.
session = request.session
can_do_action = (
"analyst_action" in session
and "analyst_action_location" in session
and session["analyst_action_location"] == domain_id
)
if not can_do_action:
return False
# Analysts may manage domains, when they are in these statuses:
valid_domain_statuses = [
DomainRequest.DomainRequestStatus.APPROVED,
DomainRequest.DomainRequestStatus.IN_REVIEW,
DomainRequest.DomainRequestStatus.REJECTED,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
# Edge case - some domains do not have
# a status or DomainInformation... aka a status of 'None'.
# It is necessary to access those to correct errors.
None,
]
requested_domain = DomainInformation.objects.filter(domain_id=domain_id).first()
# if no domain information or domain request exist, the user
# should be able to manage the domain; however, if domain information
# and domain request exist, and domain request is not in valid status,
# user should not be able to manage domain
if (
requested_domain
and requested_domain.domain_request
and requested_domain.domain_request.status not in valid_domain_statuses
):
return False
# Valid session keys exist,
# the user is permissioned,
# and it is in a valid status
return True
def _has_portfolio_view_all_domains(request, domain_pk):
"""Returns whether the user in the request can access the domain
via portfolio view all domains permission."""
portfolio = request.session.get("portfolio")
if request.user.has_view_all_domains_portfolio_permission(portfolio):
if Domain.objects.filter(id=domain_pk).exists():
domain = Domain.objects.get(id=domain_pk)
if domain.domain_info.portfolio == portfolio:
return True
return False

View file

@ -3,9 +3,13 @@ Contains middleware used in settings.py
"""
import logging
import re
from urllib.parse import parse_qs
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.urls import reverse
from django.http import HttpResponseRedirect
from django.urls import resolve
from registrar.models import User
from waffle.decorators import flag_is_active
@ -170,3 +174,51 @@ class CheckPortfolioMiddleware:
request.session["portfolio"] = request.user.get_first_portfolio()
else:
request.session["portfolio"] = request.user.get_first_portfolio()
class RestrictAccessMiddleware:
"""
Middleware that blocks access to all views unless explicitly permitted.
This middleware enforces authentication by default. Views must explicitly allow access
using access control mechanisms such as the `@grant_access` decorator. Exceptions are made
for Django admin views, explicitly ignored paths, and views that opt out of login requirements.
"""
def __init__(self, get_response):
self.get_response = get_response
# Compile regex patterns from settings to identify paths that bypass login requirements
self.ignored_paths = [re.compile(pattern) for pattern in getattr(settings, "LOGIN_REQUIRED_IGNORE_PATHS", [])]
def __call__(self, request):
# Allow requests to Django Debug Toolbar
if request.path.startswith("/__debug__/"):
return self.get_response(request)
# Allow requests matching configured ignored paths
if any(pattern.match(request.path) for pattern in self.ignored_paths):
return self.get_response(request)
# Attempt to resolve the request path to a view function
try:
resolver_match = resolve(request.path_info)
view_func = resolver_match.func
app_name = resolver_match.app_name # Get the app name of the resolved view
except Exception:
# If resolution fails, allow the request to proceed (avoid blocking non-view routes)
return self.get_response(request)
# Automatically allow access to Django's built-in admin views (excluding custom /admin/* views)
if app_name == "admin":
return self.get_response(request)
# Allow access if the view explicitly opts out of login requirements
if getattr(view_func, "login_required", True) is False:
return self.get_response(request)
# Restrict access to views that do not explicitly declare access rules
if not getattr(view_func, "has_explicit_access", False):
raise PermissionDenied # Deny access if the view lacks explicit permission handling
return self.get_response(request)

View file

@ -4,24 +4,22 @@
{% for app in app_list %}
<div class="app-{{ app.app_label }} module{% if app.app_url in request.path|urlencode %} current-app{% endif %}">
<table>
{# .gov override: add headers #}
{% if show_changelinks %}
<colgroup span="3"></colgroup>
{% else %}
<colgroup span="2"></colgroup>
{% endif %}
{# .gov override: display the app name as a caption rather than a table header #}
<caption class="text-bold">{{ app.name }}</caption>
<thead>
<tr>
{% if show_changelinks %}
<th colspan="3" class="primary-th" scope="colgroup">
{{ app.name }}
</th>
{% else %}
<th colspan="2" class="primary-th" scope="colgroup">
{{ app.name }}
</th>
{% endif %}
{# .gov override: hide headers #}
{% comment %}
{% if show_changelinks %}
<th colspan="3" class="primary-th" scope="colgroup">
{{ app.name }}
</th>
{% else %}
<th colspan="2" class="primary-th" scope="colgroup">
{{ app.name }}
</th>
{% endif %}
{% endcomment %}
</tr>
<tr>
<th scope="col">Model</th>
@ -45,16 +43,17 @@
{% endif %}
{% if model.add_url %}
<td><a href="{{ model.add_url }}" class="addlink">{% translate 'Add' %}</a></td>
{% comment %} Remove the 's' from the end of the string to avoid text like "Add domain requests" {% endcomment %}
<td><a href="{{ model.add_url }}" class="addlink" aria-label="Add {{ model.name|slice:":-1" }}">{% translate 'Add' %}</a></td>
{% else %}
<td></td>
{% endif %}
{% if model.admin_url and show_changelinks %}
{% if model.view_only %}
<td><a href="{{ model.admin_url }}" class="viewlink">{% translate 'View' %}</a></td>
<td><a href="{{ model.admin_url }}" class="viewlink" aria-label="View {{ model.name }}">{% translate 'View' %}</a></td>
{% else %}
<td><a href="{{ model.admin_url }}" class="changelink">{% translate 'Change' %}</a></td>
<td><a href="{{ model.admin_url }}" class="changelink" aria-label="Change {{ model.name }}">{% translate 'Change' %}</a></td>
{% endif %}
{% elif show_changelinks %}
<td></td>
@ -64,9 +63,20 @@
</table>
</div>
{% endfor %}
<div class="module module--custom">
<h2>Analytics</h2>
<a class="display-block padding-y-1 padding-x-1" href="{% url 'analytics' %}">Dashboard</a>
<div class="module">
<table class="width-full">
<caption class="text-bold">Analytics</caption>
<thead>
<tr>
<th scope="col">Reports</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row"><a href="{% url 'analytics' %}">Dashboard</a></th>
</tr>
</tbody>
</table>
</div>
{% else %}
<p>{% translate 'You dont have permission to view or edit anything.' %}</p>

View file

@ -16,10 +16,10 @@
<a href="{% url 'domains' %}" class="usa-breadcrumb__link"><span>Domains</span></a>
</li>
<li class="usa-breadcrumb__list-item">
<a href="{% url 'domain' pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
<a href="{% url 'domain' domain_pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
</li>
<li class="usa-breadcrumb__list-item">
<a href="{% url 'domain-users' pk=domain.id %}" class="usa-breadcrumb__link"><span>Domain managers</span></a>
<a href="{% url 'domain-users' domain_pk=domain.id %}" class="usa-breadcrumb__link"><span>Domain managers</span></a>
</li>
<li class="usa-breadcrumb__list-item usa-current" aria-current="page">
<span>Add a domain manager</span>
@ -27,7 +27,7 @@
</ol>
</nav>
{% else %}
{% url 'domain-users' pk=domain.id as url %}
{% url 'domain-users' domain_pk=domain.id as url %}
<nav class="usa-breadcrumb padding-top-0" aria-label="Domain manager breadcrumb">
<ol class="usa-breadcrumb__list">
<li class="usa-breadcrumb__list-item">

View file

@ -48,11 +48,11 @@
<p class="margin-y-0 text-primary-darker">
{% if domain.is_expired and is_domain_manager %}
This domain has expired, but it is still online.
{% url 'domain-renewal' pk=domain.id as url %}
{% url 'domain-renewal' domain_pk=domain.id as url %}
<a href="{{ url }}" class="usa-link">Renew to maintain access.</a>
{% elif domain.is_expiring and is_domain_manager %}
This domain will expire soon.
{% url 'domain-renewal' pk=domain.id as url %}
{% url 'domain-renewal' domain_pk=domain.id as url %}
<a href="{{ url }}" class="usa-link">Renew to maintain access.</a>
{% elif domain.is_expiring and is_portfolio_user %}
This domain will expire soon. Contact one of the listed domain managers to renew the domain.
@ -82,7 +82,7 @@
{% endif %}
{% url 'domain-dns-nameservers' pk=domain.id as url %}
{% url 'domain-dns-nameservers' domain_pk=domain.id as url %}
{% if domain.nameservers|length > 0 %}
{% include "includes/summary_item.html" with title='DNS name servers' domains='true' value=domain.nameservers list='true' edit_link=url editable=is_editable %}
{% else %}
@ -95,7 +95,7 @@
{% endif %}
{% endif %}
{% url 'domain-dns-dnssec' pk=domain.id as url %}
{% url 'domain-dns-dnssec' domain_pk=domain.id as url %}
{% if domain.dnssecdata is not None %}
{% include "includes/summary_item.html" with title='DNSSEC' value='Enabled' edit_link=url editable=is_editable %}
{% else %}
@ -104,26 +104,26 @@
{% if portfolio %}
{% if has_any_domains_portfolio_permission and has_edit_portfolio_permission %}
{% url 'domain-suborganization' pk=domain.id as url %}
{% url 'domain-suborganization' domain_pk=domain.id as url %}
{% include "includes/summary_item.html" with title='Suborganization' value=domain.domain_info.sub_organization edit_link=url editable=is_editable|and:has_edit_portfolio_permission %}
{% elif has_any_domains_portfolio_permission and has_view_portfolio_permission %}
{% url 'domain-suborganization' pk=domain.id as url %}
{% url 'domain-suborganization' domain_pk=domain.id as url %}
{% include "includes/summary_item.html" with title='Suborganization' value=domain.domain_info.sub_organization edit_link=url editable=is_editable|and:has_view_portfolio_permission view_button=True %}
{% endif %}
{% else %}
{% url 'domain-org-name-address' pk=domain.id as url %}
{% url 'domain-org-name-address' domain_pk=domain.id as url %}
{% include "includes/summary_item.html" with title='Organization' value=domain.domain_info address='true' edit_link=url editable=is_editable %}
{% url 'domain-senior-official' pk=domain.id as url %}
{% url 'domain-senior-official' domain_pk=domain.id as url %}
{% include "includes/summary_item.html" with title='Senior official' value=domain.domain_info.senior_official contact='true' edit_link=url editable=is_editable %}
{% endif %}
{% url 'domain-security-email' pk=domain.id as url %}
{% url 'domain-security-email' domain_pk=domain.id as url %}
{% if security_email is not None and security_email not in hidden_security_emails%}
{% include "includes/summary_item.html" with title='Security email' value=security_email edit_link=url editable=is_editable %}
{% else %}
{% include "includes/summary_item.html" with title='Security email' value='None provided' edit_link=url editable=is_editable %}
{% endif %}
{% url 'domain-users' pk=domain.id as url %}
{% url 'domain-users' domain_pk=domain.id as url %}
{% if portfolio %}
{% include "includes/summary_item.html" with title='Domain managers' domain_permissions=True value=domain edit_link=url editable=is_editable %}
{% else %}

View file

@ -13,7 +13,7 @@
<a href="{% url 'domains' %}" class="usa-breadcrumb__link"><span>Domains</span></a>
</li>
<li class="usa-breadcrumb__list-item">
<a href="{% url 'domain' pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
<a href="{% url 'domain' domain_pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
</li>
<li class="usa-breadcrumb__list-item usa-current" aria-current="page">
<span>DNS</span>
@ -30,14 +30,14 @@
<p>You can enter your name servers, as well as other DNS-related information, in the following sections:</p>
{% url 'domain-dns-nameservers' pk=domain.id as url %}
{% url 'domain-dns-nameservers' domain_pk=domain.id as url %}
<ul class="usa-list">
<li><a href="{{ url }}">Name servers</a></li>
{% url 'domain-dns-dnssec' pk=domain.id as url %}
{% url 'domain-dns-dnssec' domain_pk=domain.id as url %}
<li><a href="{{ url }}">DNSSEC</a></li>
{% if dns_prototype_flag and is_valid_domain %}
<li><a href="{% url 'prototype-domain-dns' pk=domain.id %}">Prototype DNS record creator</a></li>
<li><a href="{% url 'prototype-domain-dns' domain_pk=domain.id %}">Prototype DNS record creator</a></li>
{% endif %}
</ul>

View file

@ -14,10 +14,10 @@
<a href="{% url 'domains' %}" class="usa-breadcrumb__link"><span>Domains</span></a>
</li>
<li class="usa-breadcrumb__list-item">
<a href="{% url 'domain' pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
<a href="{% url 'domain' domain_pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
</li>
<li class="usa-breadcrumb__list-item">
<a href="{% url 'domain-dns' pk=domain.id %}" class="usa-breadcrumb__link"><span>DNS</span></a>
<a href="{% url 'domain-dns' domain_pk=domain.id %}" class="usa-breadcrumb__link"><span>DNS</span></a>
</li>
<li class="usa-breadcrumb__list-item usa-current" aria-current="page">
<span>DNSSEC</span>
@ -69,7 +69,7 @@
<p class="margin-y-0">It is strongly recommended that you only enable DNSSEC if you know how to set it up properly at your hosting service. If you make a mistake, it could cause your domain name to stop working.</p>
</div>
</div>
<a href="{% url 'domain-dns-dnssec-dsdata' pk=domain.id %}" class="usa-button">Enable DNSSEC</a>
<a href="{% url 'domain-dns-dnssec-dsdata' domain_pk=domain.id %}" class="usa-button">Enable DNSSEC</a>
</div>
{% endif %}
</form>

View file

@ -18,13 +18,13 @@
<a href="{% url 'domains' %}" class="usa-breadcrumb__link"><span>Domains</span></a>
</li>
<li class="usa-breadcrumb__list-item">
<a href="{% url 'domain' pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
<a href="{% url 'domain' domain_pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
</li>
<li class="usa-breadcrumb__list-item">
<a href="{% url 'domain-dns' pk=domain.id %}" class="usa-breadcrumb__link"><span>DNS</span></a>
<a href="{% url 'domain-dns' domain_pk=domain.id %}" class="usa-breadcrumb__link"><span>DNS</span></a>
</li>
<li class="usa-breadcrumb__list-item">
<a href="{% url 'domain-dns-dnssec' pk=domain.id %}" class="usa-breadcrumb__link"><span>DNSSEC</span></a>
<a href="{% url 'domain-dns-dnssec' domain_pk=domain.id %}" class="usa-breadcrumb__link"><span>DNSSEC</span></a>
</li>
<li class="usa-breadcrumb__list-item usa-current" aria-current="page">
<span>DS data</span>

View file

@ -19,10 +19,10 @@
<a href="{% url 'domains' %}" class="usa-breadcrumb__link"><span>Domains</span></a>
</li>
<li class="usa-breadcrumb__list-item">
<a href="{% url 'domain' pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
<a href="{% url 'domain' domain_pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
</li>
<li class="usa-breadcrumb__list-item">
<a href="{% url 'domain-dns' pk=domain.id %}" class="usa-breadcrumb__link"><span>DNS</span></a>
<a href="{% url 'domain-dns' domain_pk=domain.id %}" class="usa-breadcrumb__link"><span>DNS</span></a>
</li>
<li class="usa-breadcrumb__list-item usa-current" aria-current="page">
<span>DNS name servers</span>

View file

@ -24,7 +24,7 @@
<a href="{% url 'domains' %}" class="usa-breadcrumb__link"><span>Domains</span></a>
</li>
<li class="usa-breadcrumb__list-item">
<a href="{% url 'domain' pk=domain.id %}" class="usa-breadcrumb__link"><span>{{domain.name}}</span></a>
<a href="{% url 'domain' domain_pk=domain.id %}" class="usa-breadcrumb__link"><span>{{domain.name}}</span></a>
</li>
<li class="usa-breadcrumb__list-item usa-current" aria-current="page">
<span>Renewal Form</span>
@ -63,14 +63,14 @@
{% endif %}
{% endif %}
{% url 'domain-security-email' pk=domain.id as url %}
{% url 'domain-security-email' domain_pk=domain.id as url %}
{% if security_email is not None and security_email not in hidden_security_emails%}
{% include "includes/summary_item.html" with title='Security email' value=security_email custom_text_for_value_none='We strongly recommend that you provide a security email. This email will allow the public to report observed or suspected security issues on your domain.' edit_link=url editable=is_editable %}
{% else %}
{% include "includes/summary_item.html" with title='Security email' value='None provided' custom_text_for_value_none='We strongly recommend that you provide a security email. This email will allow the public to report observed or suspected security issues on your domain.' edit_link=url editable=is_editable %}
{% endif %}
{% url 'domain-users' pk=domain.id as url %}
{% url 'domain-users' domain_pk=domain.id as url %}
{% if portfolio %}
{% include "includes/summary_item.html" with title='Domain managers' domain_permissions=True value=domain edit_link=url editable=is_editable %}
{% else %}
@ -91,7 +91,7 @@
Acknowledgement of .gov domain requirements </h3>
</legend>
<form method="post" action="{% url 'domain-renewal' pk=domain.id %}">
<form method="post" action="{% url 'domain-renewal' domain_pk=domain.id %}">
{% csrf_token %}
<div class="usa-checkbox">

View file

@ -20,7 +20,7 @@
{% endwith %}
{% endfor %}
<button type="submit" name="submit_button" value="save" class="usa-button usa-button--with-icon usa-button--unstyled">
<button id="submit-domain-request--site-button" type="submit" name="submit_button" value="save" class="usa-button usa-button--with-icon usa-button--unstyled">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24" height="24">
<use xlink:href="{%static 'img/sprite.svg'%}#add_circle"></use>
</svg><span class="margin-left-05">Add another site</span>

View file

@ -36,7 +36,7 @@
</ol>
</nav>
{% elif steps.prev %}
<a href="{% namespaced_url 'domain-request' steps.prev id=domain_request_id %}" class="breadcrumb__back">
<a href="{% namespaced_url 'domain-request' steps.prev domain_request_pk=domain_request_id %}" class="breadcrumb__back">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24" height="24">
<use xlink:href="{%static 'img/sprite.svg'%}#arrow_back"></use>
</svg><span class="margin-left-05">Previous step</span>

View file

@ -15,7 +15,7 @@
</svg>
{% endif %}
{% endif %}
<a href="{% namespaced_url 'domain-request' this_step id=domain_request_id %}"
<a href="{% namespaced_url 'domain-request' this_step domain_request_pk=domain_request_id %}"
{% if this_step == steps.current %}
class="usa-current"
{% else %}

View file

@ -17,8 +17,8 @@
<p>If you withdraw your request, we won't review it. Once you withdraw your request, you can edit it and submit it again. </p>
<p><a href="{% url 'domain-request-withdrawn' DomainRequest.id %}" class="usa-button withdraw">Withdraw request</a>
<a href="{% url 'domain-request-status' DomainRequest.id %}">Cancel</a></p>
<p><a href="{% url 'domain-request-withdrawn' domain_request_pk=DomainRequest.id %}" class="usa-button withdraw">Withdraw request</a>
<a href="{% url 'domain-request-status' domain_request_pk=DomainRequest.id %}">Cancel</a></p>
</div>
</div>

View file

@ -16,7 +16,7 @@
<a href="{% url 'domains' %}" class="usa-breadcrumb__link"><span>Domains</span></a>
</li>
<li class="usa-breadcrumb__list-item">
<a href="{% url 'domain' pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
<a href="{% url 'domain' domain_pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
</li>
<li class="usa-breadcrumb__list-item usa-current" aria-current="page">
<span>Security email</span>

View file

@ -17,14 +17,14 @@
{% endif %}
<li class="usa-sidenav__item">
{% url 'domain-dns' pk=domain.id as url %}
{% url 'domain-dns' domain_pk=domain.id as url %}
<a href="{{ url }}" {% if request.path|startswith:url %}class="usa-current"{% endif %}">
DNS
</a>
{% if request.path|startswith:url %}
<ul class="usa-sidenav__sublist">
<li class="usa-sidenav__item">
{% url 'domain-dns-nameservers' pk=domain.id as url %}
{% url 'domain-dns-nameservers' domain_pk=domain.id as url %}
<a href="{{ url }}"
{% if request.path == url %}class="usa-current"{% endif %}
>
@ -33,7 +33,7 @@
</li>
<li class="usa-sidenav__item">
{% url 'domain-dns-dnssec' pk=domain.id as url %}
{% url 'domain-dns-dnssec' domain_pk=domain.id as url %}
<a href="{{ url }}"
{% if request.path|startswith:url %}class="usa-current"{% endif %}
>
@ -43,7 +43,7 @@
{% if domain.dnssecdata is not None or request.path|startswith:url and request.path|endswith:'dsdata' %}
<ul class="usa-sidenav__sublist">
<li class="usa-sidenav__item">
{% url 'domain-dns-dnssec-dsdata' pk=domain.id as url %}
{% url 'domain-dns-dnssec-dsdata' domain_pk=domain.id as url %}
<a href="{{ url }}"
{% if request.path == url %}class="usa-current"{% endif %}
>

View file

@ -16,7 +16,7 @@
<a href="{% url 'domains' %}" class="usa-breadcrumb__link"><span>Domains</span></a>
</li>
<li class="usa-breadcrumb__list-item">
<a href="{% url 'domain' pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
<a href="{% url 'domain' domain_pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
</li>
<li class="usa-breadcrumb__list-item usa-current" aria-current="page">
<span>Suborganization</span>

View file

@ -13,7 +13,7 @@
<a href="{% url 'domains' %}" class="usa-breadcrumb__link"><span>Domains</span></a>
</li>
<li class="usa-breadcrumb__list-item">
<a href="{% url 'domain' pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
<a href="{% url 'domain' domain_pk=domain.id %}" class="usa-breadcrumb__link"><span>{{ domain.name }}</span></a>
</li>
<li class="usa-breadcrumb__list-item usa-current" aria-current="page">
<span>Domain managers</span>
@ -93,7 +93,7 @@
{% include 'includes/modal.html' with modal_heading="Are you sure you want to remove yourself as a domain manager?" modal_description="You will no longer be able to manage the domain <strong>"|add:domain_name|add:"</strong>."|safe modal_button_id="user-delete-button-"|add:counter_str|safe modal_button_text="Yes, remove myself" modal_button_class="usa-button--secondary" %}
{% endwith %}
</div>
<form method="POST" id="user-delete-form-{{ forloop.counter }}" action="{% url "domain-user-delete" pk=domain.id user_pk=item.permission.user.id %}" >
<form method="POST" id="user-delete-form-{{ forloop.counter }}" action="{% url "domain-user-delete" domain_pk=domain.id user_pk=item.permission.user.id %}" >
{% csrf_token %}
</form>
{% else %}
@ -108,7 +108,7 @@
{% include 'includes/modal.html' with modal_heading="Are you sure you want to remove " heading_value=email|add:"?" modal_description="<strong>"|add:email|add:"</strong> will no longer be able to manage the domain <strong>"|add:domain_name|add:"</strong>."|safe modal_button_id="user-delete-button-"|add:counter_str|safe modal_button_text="Yes, remove domain manager" modal_button_class="usa-button--secondary" %}
{% endwith %}
</div>
<form method="POST" id="user-delete-form-{{ forloop.counter }}" action="{% url "domain-user-delete" pk=domain.id user_pk=item.permission.user.id %}">
<form method="POST" id="user-delete-form-{{ forloop.counter }}" action="{% url "domain-user-delete" domain_pk=domain.id user_pk=item.permission.user.id %}">
{% csrf_token %}
</form>
{% endif %}
@ -123,7 +123,7 @@
></div>
{% endif %}
<a class="usa-button usa-button--unstyled usa-button--with-icon" href="{% url 'domain-users-add' pk=domain.id %}">
<a class="usa-button usa-button--unstyled usa-button--with-icon" href="{% url 'domain-users-add' domain_pk=domain.id %}">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24" height="24">
<use xlink:href="{%static 'img/sprite.svg'%}#add_circle"></use>
</svg><span class="margin-left-05">Add a domain manager</span>
@ -154,7 +154,7 @@
{% if not portfolio %}<td data-label="Status">{{ invitation.domain_invitation.status|title }}</td>{% endif %}
<td>
{% if invitation.domain_invitation.status == invitation.domain_invitation.DomainInvitationStatus.INVITED %}
<form method="POST" action="{% url "invitation-cancel" pk=invitation.domain_invitation.id %}">
<form method="POST" action="{% url "invitation-cancel" domain_invitation_pk=invitation.domain_invitation.id %}">
{% csrf_token %}<input type="submit" class="usa-button--unstyled text-no-underline cursor-pointer" value="Cancel">
</form>
{% endif %}

View file

@ -1,6 +1,6 @@
<li class="usa-sidenav__item">
{% if url_name %}
{% url url_name pk=domain.id as url %}
{% url url_name domain_pk=domain.id as url %}
{% endif %}
<a href="{{ url }}"
{% if request.path == url %}class="usa-current"{% endif %}

View file

@ -45,7 +45,7 @@
<caption class="sr-only">member domains</caption>
<thead>
<tr>
<th data-sortable="checked" scope="col" role="columnheader" class="padding-right-105 width-6"><span class="sr-only">Assigned domains</span></th>
<th data-sortable="checked" scope="col" role="columnheader" class="padding-right-105 width-6 left-align-sort-button"><span class="sr-only">Assigned domains</span></th>
<!-- We override default sort to be name/ascending in the JSON endpoint. We add the correct aria-sort attribute here to reflect that in the UI -->
<th data-sortable="name" scope="col" role="columnheader" aria-sort="descending">Domains</th>
</tr>

View file

@ -4,7 +4,7 @@
{% for step in steps %}
<section class="summary-item margin-top-3">
{% if is_editable %}
{% namespaced_url 'domain-request' step id=domain_request_id as domain_request_url %}
{% namespaced_url 'domain-request' step domain_request_pk=domain_request_id as domain_request_url %}
{% endif %}
{% if step == Step.REQUESTING_ENTITY %}

View file

@ -4,7 +4,7 @@
{% for step in steps %}
<section class="summary-item margin-top-3">
{% if is_editable %}
{% namespaced_url 'domain-request' step id=domain_request_id as domain_request_url %}
{% namespaced_url 'domain-request' step domain_request_pk=domain_request_id as domain_request_url %}
{% endif %}
{% if step == Step.ORGANIZATION_TYPE %}

View file

@ -114,7 +114,7 @@
{% block modify_request %}
{% if DomainRequest.is_withdrawable %}
<p><a href="{% url 'domain-request-withdraw-confirmation' pk=DomainRequest.id %}" class="usa-button usa-button--outline withdraw_outline">
<p><a href="{% url 'domain-request-withdraw-confirmation' domain_request_pk=DomainRequest.id %}" class="usa-button usa-button--outline withdraw_outline">
Withdraw request</a>
</p>
{% endif %}

View file

@ -3834,8 +3834,6 @@ class TestTransferUser(WebTest):
self.assertContains(after_submit, "<h1>Change user</h1>")
print(mock_success_message.call_args_list)
mock_success_message.assert_any_call(
ANY,
(

View file

@ -224,7 +224,7 @@ class TestDomainAdminAsStaff(MockEppLib):
self.assertEqual(domain.state, Domain.State.DELETED)
# @less_console_noise_decorator
@less_console_noise_decorator
def test_deletion_is_unsuccessful(self):
"""
Scenario: Domain deletion is unsuccessful
@ -881,7 +881,7 @@ class TestDomainAdminWithClient(TestCase):
response = self.client.get("/admin/registrar/domain/")
# There are 4 template references to Federal (4) plus four references in the table
# for our actual domain_request
self.assertContains(response, "Federal", count=56)
self.assertContains(response, "Federal", count=57)
# This may be a bit more robust
self.assertContains(response, '<td class="field-converted_generic_org_type">Federal</td>', count=1)
# Now let's make sure the long description does not exist

View file

@ -662,7 +662,7 @@ class TestDomainRequestAdmin(MockEppLib):
response = self.client.get("/admin/registrar/domainrequest/?generic_org_type__exact=federal")
# There are 2 template references to Federal (4) and two in the results data
# of the request
self.assertContains(response, "Federal", count=54)
self.assertContains(response, "Federal", count=55)
# This may be a bit more robust
self.assertContains(response, '<td class="field-converted_generic_org_type">Federal</td>', count=1)
# Now let's make sure the long description does not exist

View file

@ -62,7 +62,7 @@ class GetSeniorOfficialJsonTest(TestCase):
p = "password"
self.client.login(username="testuser", password=p)
response = self.client.get(self.api_url, {"agency_name": "Test Agency"})
self.assertEqual(response.status_code, 302)
self.assertEqual(response.status_code, 403)
@less_console_noise_decorator
def test_get_senior_official_json_not_found(self):
@ -138,7 +138,7 @@ class GetPortfolioJsonTest(TestCase):
"""Test that an unauthenticated user receives a 403 with an error message."""
self.client.force_login(self.user)
response = self.client.get(self.api_url, {"id": self.portfolio.id})
self.assertEqual(response.status_code, 302)
self.assertEqual(response.status_code, 403)
@less_console_noise_decorator
def test_get_portfolio_json_not_found(self):
@ -181,7 +181,7 @@ class GetFederalPortfolioTypeJsonTest(TestCase):
p = "password"
self.client.login(username="testuser", password=p)
response = self.client.get(self.api_url, {"agency_name": "Test Agency", "organization_type": "federal"})
self.assertEqual(response.status_code, 302)
self.assertEqual(response.status_code, 403)
class GetActionNeededEmailForUserJsonTest(TestCase):

View file

@ -38,7 +38,6 @@ from epplibwrapper import commands, common
from .common import (
MockEppLib,
less_console_noise,
completed_domain_request,
MockSESClient,
MockDbForIndividualTests,
@ -454,6 +453,7 @@ class TestPopulateFirstReady(TestCase):
# Delete domains
Domain.objects.all().delete()
@less_console_noise_decorator
def run_populate_first_ready(self):
"""
This method executes the populate_first_ready command.
@ -461,103 +461,102 @@ class TestPopulateFirstReady(TestCase):
The 'call_command' function from Django's management framework is then used to
execute the populate_first_ready command with the specified arguments.
"""
with less_console_noise():
with patch(
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa
return_value=True,
):
call_command("populate_first_ready")
with patch(
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa
return_value=True,
):
call_command("populate_first_ready")
@less_console_noise_decorator
def test_populate_first_ready_state_ready(self):
"""
Tests that the populate_first_ready works as expected for the state 'ready'
"""
with less_console_noise():
# Set the created at date
self.ready_domain.created_at = self.ready_at_date_tz_aware
self.ready_domain.save()
desired_domain = copy.deepcopy(self.ready_domain)
desired_domain.first_ready = self.ready_at_date
# Run the expiration date script
self.run_populate_first_ready()
self.assertEqual(desired_domain, self.ready_domain)
# Explicitly test the first_ready date
first_ready = Domain.objects.filter(name="fakeready.gov").get().first_ready
self.assertEqual(first_ready, self.ready_at_date)
# Set the created at date
self.ready_domain.created_at = self.ready_at_date_tz_aware
self.ready_domain.save()
desired_domain = copy.deepcopy(self.ready_domain)
desired_domain.first_ready = self.ready_at_date
# Run the expiration date script
self.run_populate_first_ready()
self.assertEqual(desired_domain, self.ready_domain)
# Explicitly test the first_ready date
first_ready = Domain.objects.filter(name="fakeready.gov").get().first_ready
self.assertEqual(first_ready, self.ready_at_date)
@less_console_noise_decorator
def test_populate_first_ready_state_deleted(self):
"""
Tests that the populate_first_ready works as expected for the state 'deleted'
"""
with less_console_noise():
# Set the created at date
self.deleted_domain.created_at = self.ready_at_date_tz_aware
self.deleted_domain.save()
desired_domain = copy.deepcopy(self.deleted_domain)
desired_domain.first_ready = self.ready_at_date
# Run the expiration date script
self.run_populate_first_ready()
self.assertEqual(desired_domain, self.deleted_domain)
# Explicitly test the first_ready date
first_ready = Domain.objects.filter(name="fakedeleted.gov").get().first_ready
self.assertEqual(first_ready, self.ready_at_date)
# Set the created at date
self.deleted_domain.created_at = self.ready_at_date_tz_aware
self.deleted_domain.save()
desired_domain = copy.deepcopy(self.deleted_domain)
desired_domain.first_ready = self.ready_at_date
# Run the expiration date script
self.run_populate_first_ready()
self.assertEqual(desired_domain, self.deleted_domain)
# Explicitly test the first_ready date
first_ready = Domain.objects.filter(name="fakedeleted.gov").get().first_ready
self.assertEqual(first_ready, self.ready_at_date)
@less_console_noise_decorator
def test_populate_first_ready_state_dns_needed(self):
"""
Tests that the populate_first_ready doesn't make changes when a domain's state is 'dns_needed'
"""
with less_console_noise():
# Set the created at date
self.dns_needed_domain.created_at = self.ready_at_date_tz_aware
self.dns_needed_domain.save()
desired_domain = copy.deepcopy(self.dns_needed_domain)
desired_domain.first_ready = None
# Run the expiration date script
self.run_populate_first_ready()
current_domain = self.dns_needed_domain
# The object should largely be unaltered (does not test first_ready)
self.assertEqual(desired_domain, current_domain)
first_ready = Domain.objects.filter(name="fakedns.gov").get().first_ready
# Explicitly test the first_ready date
self.assertNotEqual(first_ready, self.ready_at_date)
self.assertEqual(first_ready, None)
# Set the created at date
self.dns_needed_domain.created_at = self.ready_at_date_tz_aware
self.dns_needed_domain.save()
desired_domain = copy.deepcopy(self.dns_needed_domain)
desired_domain.first_ready = None
# Run the expiration date script
self.run_populate_first_ready()
current_domain = self.dns_needed_domain
# The object should largely be unaltered (does not test first_ready)
self.assertEqual(desired_domain, current_domain)
first_ready = Domain.objects.filter(name="fakedns.gov").get().first_ready
# Explicitly test the first_ready date
self.assertNotEqual(first_ready, self.ready_at_date)
self.assertEqual(first_ready, None)
@less_console_noise_decorator
def test_populate_first_ready_state_on_hold(self):
"""
Tests that the populate_first_ready works as expected for the state 'on_hold'
"""
with less_console_noise():
self.hold_domain.created_at = self.ready_at_date_tz_aware
self.hold_domain.save()
desired_domain = copy.deepcopy(self.hold_domain)
desired_domain.first_ready = self.ready_at_date
# Run the update first ready_at script
self.run_populate_first_ready()
current_domain = self.hold_domain
self.assertEqual(desired_domain, current_domain)
# Explicitly test the first_ready date
first_ready = Domain.objects.filter(name="fakehold.gov").get().first_ready
self.assertEqual(first_ready, self.ready_at_date)
self.hold_domain.created_at = self.ready_at_date_tz_aware
self.hold_domain.save()
desired_domain = copy.deepcopy(self.hold_domain)
desired_domain.first_ready = self.ready_at_date
# Run the update first ready_at script
self.run_populate_first_ready()
current_domain = self.hold_domain
self.assertEqual(desired_domain, current_domain)
# Explicitly test the first_ready date
first_ready = Domain.objects.filter(name="fakehold.gov").get().first_ready
self.assertEqual(first_ready, self.ready_at_date)
@less_console_noise_decorator
def test_populate_first_ready_state_unknown(self):
"""
Tests that the populate_first_ready works as expected for the state 'unknown'
"""
with less_console_noise():
# Set the created at date
self.unknown_domain.created_at = self.ready_at_date_tz_aware
self.unknown_domain.save()
desired_domain = copy.deepcopy(self.unknown_domain)
desired_domain.first_ready = None
# Run the expiration date script
self.run_populate_first_ready()
current_domain = self.unknown_domain
# The object should largely be unaltered (does not test first_ready)
self.assertEqual(desired_domain, current_domain)
# Explicitly test the first_ready date
first_ready = Domain.objects.filter(name="fakeunknown.gov").get().first_ready
self.assertNotEqual(first_ready, self.ready_at_date)
self.assertEqual(first_ready, None)
# Set the created at date
self.unknown_domain.created_at = self.ready_at_date_tz_aware
self.unknown_domain.save()
desired_domain = copy.deepcopy(self.unknown_domain)
desired_domain.first_ready = None
# Run the expiration date script
self.run_populate_first_ready()
current_domain = self.unknown_domain
# The object should largely be unaltered (does not test first_ready)
self.assertEqual(desired_domain, current_domain)
# Explicitly test the first_ready date
first_ready = Domain.objects.filter(name="fakeunknown.gov").get().first_ready
self.assertNotEqual(first_ready, self.ready_at_date)
self.assertEqual(first_ready, None)
class TestPatchAgencyInfo(TestCase):
@ -578,10 +577,10 @@ class TestPatchAgencyInfo(TestCase):
TransitionDomain.objects.all().delete()
@patch("registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", return_value=True)
@less_console_noise_decorator
def call_patch_federal_agency_info(self, mock_prompt):
"""Calls the patch_federal_agency_info command and mimics a keypress"""
with less_console_noise():
call_command("patch_federal_agency_info", "registrar/tests/data/fake_current_full.csv", debug=True)
call_command("patch_federal_agency_info", "registrar/tests/data/fake_current_full.csv", debug=True)
class TestExtendExpirationDates(MockEppLib):
@ -637,6 +636,7 @@ class TestExtendExpirationDates(MockEppLib):
User.objects.all().delete()
UserDomainRole.objects.all().delete()
@less_console_noise_decorator
def run_extend_expiration_dates(self):
"""
This method executes the extend_expiration_dates command.
@ -644,83 +644,83 @@ class TestExtendExpirationDates(MockEppLib):
The 'call_command' function from Django's management framework is then used to
execute the extend_expiration_dates command with the specified arguments.
"""
with less_console_noise():
with patch(
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa
return_value=True,
):
call_command("extend_expiration_dates")
with patch(
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa
return_value=True,
):
call_command("extend_expiration_dates")
@less_console_noise_decorator
def test_extends_expiration_date_correctly(self):
"""
Tests that the extend_expiration_dates method extends dates as expected
"""
with less_console_noise():
desired_domain = Domain.objects.filter(name="waterbutpurple.gov").get()
desired_domain.expiration_date = date(2024, 11, 15)
# Run the expiration date script
self.run_extend_expiration_dates()
current_domain = Domain.objects.filter(name="waterbutpurple.gov").get()
self.assertEqual(desired_domain, current_domain)
# Explicitly test the expiration date
self.assertEqual(current_domain.expiration_date, date(2024, 11, 15))
desired_domain = Domain.objects.filter(name="waterbutpurple.gov").get()
desired_domain.expiration_date = date(2024, 11, 15)
# Run the expiration date script
self.run_extend_expiration_dates()
current_domain = Domain.objects.filter(name="waterbutpurple.gov").get()
self.assertEqual(desired_domain, current_domain)
# Explicitly test the expiration date
self.assertEqual(current_domain.expiration_date, date(2024, 11, 15))
@less_console_noise_decorator
def test_extends_expiration_date_skips_non_current(self):
"""
Tests that the extend_expiration_dates method correctly skips domains
with an expiration date less than a certain threshold.
"""
with less_console_noise():
desired_domain = Domain.objects.filter(name="fake.gov").get()
desired_domain.expiration_date = date(2022, 5, 25)
# Run the expiration date script
self.run_extend_expiration_dates()
current_domain = Domain.objects.filter(name="fake.gov").get()
self.assertEqual(desired_domain, current_domain)
# Explicitly test the expiration date. The extend_expiration_dates script
# will skip all dates less than date(2023, 11, 15), meaning that this domain
# should not be affected by the change.
self.assertEqual(current_domain.expiration_date, date(2022, 5, 25))
desired_domain = Domain.objects.filter(name="fake.gov").get()
desired_domain.expiration_date = date(2022, 5, 25)
# Run the expiration date script
self.run_extend_expiration_dates()
current_domain = Domain.objects.filter(name="fake.gov").get()
self.assertEqual(desired_domain, current_domain)
# Explicitly test the expiration date. The extend_expiration_dates script
# will skip all dates less than date(2023, 11, 15), meaning that this domain
# should not be affected by the change.
self.assertEqual(current_domain.expiration_date, date(2022, 5, 25))
@less_console_noise_decorator
def test_extends_expiration_date_skips_maximum_date(self):
"""
Tests that the extend_expiration_dates method correctly skips domains
with an expiration date more than a certain threshold.
"""
with less_console_noise():
desired_domain = Domain.objects.filter(name="fakemaximum.gov").get()
desired_domain.expiration_date = date(2024, 12, 31)
desired_domain = Domain.objects.filter(name="fakemaximum.gov").get()
desired_domain.expiration_date = date(2024, 12, 31)
# Run the expiration date script
self.run_extend_expiration_dates()
# Run the expiration date script
self.run_extend_expiration_dates()
current_domain = Domain.objects.filter(name="fakemaximum.gov").get()
self.assertEqual(desired_domain, current_domain)
current_domain = Domain.objects.filter(name="fakemaximum.gov").get()
self.assertEqual(desired_domain, current_domain)
# Explicitly test the expiration date. The extend_expiration_dates script
# will skip all dates less than date(2023, 11, 15), meaning that this domain
# should not be affected by the change.
self.assertEqual(current_domain.expiration_date, date(2024, 12, 31))
# Explicitly test the expiration date. The extend_expiration_dates script
# will skip all dates less than date(2023, 11, 15), meaning that this domain
# should not be affected by the change.
self.assertEqual(current_domain.expiration_date, date(2024, 12, 31))
@less_console_noise_decorator
def test_extends_expiration_date_skips_non_ready(self):
"""
Tests that the extend_expiration_dates method correctly skips domains not in the state "ready"
"""
with less_console_noise():
desired_domain = Domain.objects.filter(name="fakeneeded.gov").get()
desired_domain.expiration_date = date(2023, 11, 15)
desired_domain = Domain.objects.filter(name="fakeneeded.gov").get()
desired_domain.expiration_date = date(2023, 11, 15)
# Run the expiration date script
self.run_extend_expiration_dates()
# Run the expiration date script
self.run_extend_expiration_dates()
current_domain = Domain.objects.filter(name="fakeneeded.gov").get()
self.assertEqual(desired_domain, current_domain)
current_domain = Domain.objects.filter(name="fakeneeded.gov").get()
self.assertEqual(desired_domain, current_domain)
# Explicitly test the expiration date. The extend_expiration_dates script
# will skip all dates less than date(2023, 11, 15), meaning that this domain
# should not be affected by the change.
self.assertEqual(current_domain.expiration_date, date(2023, 11, 15))
# Explicitly test the expiration date. The extend_expiration_dates script
# will skip all dates less than date(2023, 11, 15), meaning that this domain
# should not be affected by the change.
self.assertEqual(current_domain.expiration_date, date(2023, 11, 15))
@less_console_noise_decorator
def test_extends_expiration_date_idempotent(self):
"""
Tests the idempotency of the extend_expiration_dates command.
@ -728,21 +728,20 @@ class TestExtendExpirationDates(MockEppLib):
Verifies that running the method multiple times does not change the expiration date
of a domain beyond the initial extension.
"""
with less_console_noise():
desired_domain = Domain.objects.filter(name="waterbutpurple.gov").get()
desired_domain.expiration_date = date(2024, 11, 15)
# Run the expiration date script
self.run_extend_expiration_dates()
current_domain = Domain.objects.filter(name="waterbutpurple.gov").get()
self.assertEqual(desired_domain, current_domain)
# Explicitly test the expiration date
self.assertEqual(desired_domain.expiration_date, date(2024, 11, 15))
# Run the expiration date script again
self.run_extend_expiration_dates()
# The old domain shouldn't have changed
self.assertEqual(desired_domain, current_domain)
# Explicitly test the expiration date - should be the same
self.assertEqual(desired_domain.expiration_date, date(2024, 11, 15))
desired_domain = Domain.objects.filter(name="waterbutpurple.gov").get()
desired_domain.expiration_date = date(2024, 11, 15)
# Run the expiration date script
self.run_extend_expiration_dates()
current_domain = Domain.objects.filter(name="waterbutpurple.gov").get()
self.assertEqual(desired_domain, current_domain)
# Explicitly test the expiration date
self.assertEqual(desired_domain.expiration_date, date(2024, 11, 15))
# Run the expiration date script again
self.run_extend_expiration_dates()
# The old domain shouldn't have changed
self.assertEqual(desired_domain, current_domain)
# Explicitly test the expiration date - should be the same
self.assertEqual(desired_domain.expiration_date, date(2024, 11, 15))
class TestDiscloseEmails(MockEppLib):
@ -754,6 +753,7 @@ class TestDiscloseEmails(MockEppLib):
PublicContact.objects.all().delete()
Domain.objects.all().delete()
@less_console_noise_decorator
def run_disclose_security_emails(self):
"""
This method executes the disclose_security_emails command.
@ -761,44 +761,43 @@ class TestDiscloseEmails(MockEppLib):
The 'call_command' function from Django's management framework is then used to
execute the disclose_security_emails command.
"""
with less_console_noise():
with patch(
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa
return_value=True,
):
call_command("disclose_security_emails")
with patch(
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa
return_value=True,
):
call_command("disclose_security_emails")
@less_console_noise_decorator
def test_disclose_security_emails(self):
"""
Tests that command disclose_security_emails runs successfully with
appropriate EPP calll to UpdateContact.
"""
with less_console_noise():
domain, _ = Domain.objects.get_or_create(name="testdisclose.gov", state=Domain.State.READY)
expectedSecContact = PublicContact.get_default_security()
expectedSecContact.domain = domain
expectedSecContact.email = "123@mail.gov"
# set domain security email to 123@mail.gov instead of default email
domain.security_contact = expectedSecContact
self.run_disclose_security_emails()
domain, _ = Domain.objects.get_or_create(name="testdisclose.gov", state=Domain.State.READY)
expectedSecContact = PublicContact.get_default_security()
expectedSecContact.domain = domain
expectedSecContact.email = "123@mail.gov"
# set domain security email to 123@mail.gov instead of default email
domain.security_contact = expectedSecContact
self.run_disclose_security_emails()
# running disclose_security_emails sends EPP call UpdateContact with disclose
self.mockedSendFunction.assert_has_calls(
[
call(
commands.UpdateContact(
id=domain.security_contact.registry_id,
postal_info=domain._make_epp_contact_postal_info(contact=domain.security_contact),
email=domain.security_contact.email,
voice=domain.security_contact.voice,
fax=domain.security_contact.fax,
auth_info=common.ContactAuthInfo(pw="2fooBAR123fooBaz"),
disclose=domain._disclose_fields(contact=domain.security_contact),
),
cleaned=True,
)
]
)
# running disclose_security_emails sends EPP call UpdateContact with disclose
self.mockedSendFunction.assert_has_calls(
[
call(
commands.UpdateContact(
id=domain.security_contact.registry_id,
postal_info=domain._make_epp_contact_postal_info(contact=domain.security_contact),
email=domain.security_contact.email,
voice=domain.security_contact.voice,
fax=domain.security_contact.fax,
auth_info=common.ContactAuthInfo(pw="2fooBAR123fooBaz"),
disclose=domain._disclose_fields(contact=domain.security_contact),
),
cleaned=True,
)
]
)
class TestCleanTables(TestCase):
@ -813,17 +812,18 @@ class TestCleanTables(TestCase):
self.logger_patcher.stop()
@override_settings(IS_PRODUCTION=True)
@less_console_noise_decorator
def test_command_logs_error_in_production(self):
"""Test that the handle method does not process in production"""
with less_console_noise():
with patch(
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa
return_value=True,
):
call_command("clean_tables")
self.logger_mock.error.assert_called_with("clean_tables cannot be run in production")
with patch(
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa
return_value=True,
):
call_command("clean_tables")
self.logger_mock.error.assert_called_with("clean_tables cannot be run in production")
@override_settings(IS_PRODUCTION=False)
@less_console_noise_decorator
def test_command_cleans_tables(self):
"""test that the handle method functions properly to clean tables"""
@ -891,61 +891,61 @@ class TestCleanTables(TestCase):
raise
@override_settings(IS_PRODUCTION=False)
@less_console_noise_decorator
def test_command_handles_nonexistent_model(self):
"""Test that exceptions for non existent models are handled properly within the handle method"""
with less_console_noise():
with patch("django.apps.apps.get_model", side_effect=LookupError):
with patch(
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa
return_value=True,
):
call_command("clean_tables")
# Assert that the error message was logged for any of the table names
self.logger_mock.error.assert_any_call("Model for table DomainInformation not found.")
self.logger_mock.error.assert_any_call("Model for table DomainRequest not found.")
self.logger_mock.error.assert_any_call("Model for table PublicContact not found.")
self.logger_mock.error.assert_any_call("Model for table Domain not found.")
self.logger_mock.error.assert_any_call("Model for table User not found.")
self.logger_mock.error.assert_any_call("Model for table Contact not found.")
self.logger_mock.error.assert_any_call("Model for table Website not found.")
self.logger_mock.error.assert_any_call("Model for table DraftDomain not found.")
self.logger_mock.error.assert_any_call("Model for table HostIp not found.")
self.logger_mock.error.assert_any_call("Model for table Host not found.")
with patch("django.apps.apps.get_model", side_effect=LookupError):
with patch(
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa
return_value=True,
):
call_command("clean_tables")
# Assert that the error message was logged for any of the table names
self.logger_mock.error.assert_any_call("Model for table DomainInformation not found.")
self.logger_mock.error.assert_any_call("Model for table DomainRequest not found.")
self.logger_mock.error.assert_any_call("Model for table PublicContact not found.")
self.logger_mock.error.assert_any_call("Model for table Domain not found.")
self.logger_mock.error.assert_any_call("Model for table User not found.")
self.logger_mock.error.assert_any_call("Model for table Contact not found.")
self.logger_mock.error.assert_any_call("Model for table Website not found.")
self.logger_mock.error.assert_any_call("Model for table DraftDomain not found.")
self.logger_mock.error.assert_any_call("Model for table HostIp not found.")
self.logger_mock.error.assert_any_call("Model for table Host not found.")
@override_settings(IS_PRODUCTION=False)
@less_console_noise_decorator
def test_command_logs_other_exceptions(self):
"""Test that generic exceptions are handled properly in the handle method"""
with less_console_noise():
with patch("django.apps.apps.get_model") as get_model_mock:
model_mock = MagicMock()
get_model_mock.return_value = model_mock
with patch("django.apps.apps.get_model") as get_model_mock:
model_mock = MagicMock()
get_model_mock.return_value = model_mock
# Mock the values_list so that DomainInformation attempts a delete
pk_batches = [[1, 2, 3, 4, 5, 6], []]
# Mock the values_list so that DomainInformation attempts a delete
pk_batches = [[1, 2, 3, 4, 5, 6], []]
def values_list_side_effect(*args, **kwargs):
if args == ("pk",) and kwargs.get("flat", False):
return pk_batches.pop(0)
return []
def values_list_side_effect(*args, **kwargs):
if args == ("pk",) and kwargs.get("flat", False):
return pk_batches.pop(0)
return []
model_mock.objects.values_list.side_effect = values_list_side_effect
model_mock.objects.values_list.side_effect = values_list_side_effect
# Mock delete to raise a generic exception
model_mock.objects.filter.return_value.delete.side_effect = Exception("Mocked delete exception")
# Mock delete to raise a generic exception
model_mock.objects.filter.return_value.delete.side_effect = Exception("Mocked delete exception")
with patch(
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit",
return_value=True,
):
with self.assertRaises(Exception) as context:
# Execute the command
call_command("clean_tables")
with patch(
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit",
return_value=True,
):
with self.assertRaises(Exception) as context:
# Execute the command
call_command("clean_tables")
# Check the exception message
self.assertEqual(str(context.exception), "Custom delete error")
# Check the exception message
self.assertEqual(str(context.exception), "Custom delete error")
# Assert that delete was called
model_mock.objects.filter.return_value.delete.assert_called()
# Assert that delete was called
model_mock.objects.filter.return_value.delete.assert_called()
class TestExportTables(MockEppLib):
@ -1030,34 +1030,34 @@ class TestExportTables(MockEppLib):
self.logger_mock.info.assert_any_call(f"Removed {table_name}_1.csv")
@patch("registrar.management.commands.export_tables.getattr")
@less_console_noise_decorator
def test_export_table_handles_missing_resource_class(self, mock_getattr):
"""Test that missing resource classes are handled properly in the handle method"""
with less_console_noise():
mock_getattr.side_effect = AttributeError
mock_getattr.side_effect = AttributeError
# Import the command to avoid any locale or gettext issues
command_class = import_string("registrar.management.commands.export_tables.Command")
command_instance = command_class()
command_instance.export_table("NonExistentTable")
# Import the command to avoid any locale or gettext issues
command_class = import_string("registrar.management.commands.export_tables.Command")
command_instance = command_class()
command_instance.export_table("NonExistentTable")
self.logger_mock.error.assert_called_with(
"Resource class NonExistentTableResource not found in registrar.admin"
)
self.logger_mock.error.assert_called_with(
"Resource class NonExistentTableResource not found in registrar.admin"
)
@patch("registrar.management.commands.export_tables.getattr")
@less_console_noise_decorator
def test_export_table_handles_generic_exception(self, mock_getattr):
"""Test that general exceptions in the handle method are handled correctly"""
with less_console_noise():
mock_resource_class = MagicMock()
mock_resource_class().export.side_effect = Exception("Test Exception")
mock_getattr.return_value = mock_resource_class
mock_resource_class = MagicMock()
mock_resource_class().export.side_effect = Exception("Test Exception")
mock_getattr.return_value = mock_resource_class
# Import the command to avoid any locale or gettext issues
command_class = import_string("registrar.management.commands.export_tables.Command")
command_instance = command_class()
command_instance.export_table("TestTable")
# Import the command to avoid any locale or gettext issues
command_class = import_string("registrar.management.commands.export_tables.Command")
command_instance = command_class()
command_instance.export_table("TestTable")
self.logger_mock.error.assert_called_with("Failed to export TestTable: Test Exception")
self.logger_mock.error.assert_called_with("Failed to export TestTable: Test Exception")
class TestImportTables(TestCase):
@ -1073,6 +1073,7 @@ class TestImportTables(TestCase):
@patch("registrar.management.commands.import_tables.getattr")
@patch("django.apps.apps.get_model")
@patch("os.listdir")
@less_console_noise_decorator
def test_handle(
self,
mock_listdir,
@ -1087,105 +1088,104 @@ class TestImportTables(TestCase):
mock_makedirs,
):
"""Test that the handle method properly imports tables"""
with less_console_noise():
# Mock os.makedirs to do nothing
mock_makedirs.return_value = None
# Mock os.makedirs to do nothing
mock_makedirs.return_value = None
# Mock os.path.exists to always return True
mock_path_exists.return_value = True
# Mock os.path.exists to always return True
mock_path_exists.return_value = True
# Mock the zipfile to have extractall return None
mock_zipfile_instance = mock_zipfile.return_value.__enter__.return_value
mock_zipfile_instance.extractall.return_value = None
# Mock the zipfile to have extractall return None
mock_zipfile_instance = mock_zipfile.return_value.__enter__.return_value
mock_zipfile_instance.extractall.return_value = None
# Check that the import_table function was called for each table
table_names = [
"User",
"Contact",
"Domain",
"DomainRequest",
"DomainInformation",
"UserDomainRole",
"DraftDomain",
"Website",
"HostIp",
"Host",
"PublicContact",
]
# Check that the import_table function was called for each table
table_names = [
"User",
"Contact",
"Domain",
"DomainRequest",
"DomainInformation",
"UserDomainRole",
"DraftDomain",
"Website",
"HostIp",
"Host",
"PublicContact",
]
# Mock directory listing
mock_listdir.side_effect = lambda path: [f"{table}_1.csv" for table in table_names]
# Mock directory listing
mock_listdir.side_effect = lambda path: [f"{table}_1.csv" for table in table_names]
# Mock the CSV file content
csv_content = b"mock_csv_data"
# Mock the CSV file content
csv_content = b"mock_csv_data"
# Mock the open function to return a mock file
mock_open.return_value.__enter__.return_value.read.return_value = csv_content
# Mock the open function to return a mock file
mock_open.return_value.__enter__.return_value.read.return_value = csv_content
# Mock the Dataset class and its load method to return a dataset
mock_dataset_instance = MagicMock(spec=tablib.Dataset)
with patch(
"registrar.management.commands.import_tables.tablib.Dataset.load", return_value=mock_dataset_instance
):
# Mock the resource class and its import method
mock_resource_class = MagicMock()
mock_resource_instance = MagicMock()
mock_result = MagicMock()
mock_result.has_errors.return_value = False
mock_resource_instance.import_data.return_value = mock_result
mock_resource_class.return_value = mock_resource_instance
mock_getattr.return_value = mock_resource_class
# Mock the Dataset class and its load method to return a dataset
mock_dataset_instance = MagicMock(spec=tablib.Dataset)
with patch(
"registrar.management.commands.import_tables.tablib.Dataset.load", return_value=mock_dataset_instance
):
# Mock the resource class and its import method
mock_resource_class = MagicMock()
mock_resource_instance = MagicMock()
mock_result = MagicMock()
mock_result.has_errors.return_value = False
mock_resource_instance.import_data.return_value = mock_result
mock_resource_class.return_value = mock_resource_instance
mock_getattr.return_value = mock_resource_class
# Call the command
call_command("import_tables")
# Call the command
call_command("import_tables")
# Check that os.makedirs was called once to create the tmp directory
mock_makedirs.assert_called_once_with("tmp", exist_ok=True)
# Check that os.makedirs was called once to create the tmp directory
mock_makedirs.assert_called_once_with("tmp", exist_ok=True)
# Check that os.path.exists was called once for the zip file
mock_path_exists.assert_any_call("tmp/exported_tables.zip")
# Check that os.path.exists was called once for the zip file
mock_path_exists.assert_any_call("tmp/exported_tables.zip")
# Check that pyzipper.AESZipFile was called once to open the zip file
mock_zipfile.assert_called_once_with("tmp/exported_tables.zip", "r")
# Check that pyzipper.AESZipFile was called once to open the zip file
mock_zipfile.assert_called_once_with("tmp/exported_tables.zip", "r")
# Check that extractall was called once to extract the zip file contents
mock_zipfile_instance.extractall.assert_called_once_with("tmp")
# Check that extractall was called once to extract the zip file contents
mock_zipfile_instance.extractall.assert_called_once_with("tmp")
# Check that os.path.exists was called for each table
for table_name in table_names:
mock_path_exists.assert_any_call(f"{table_name}_1.csv")
# Check that os.path.exists was called for each table
for table_name in table_names:
mock_path_exists.assert_any_call(f"{table_name}_1.csv")
# Check that logger.info was called for each successful import
for table_name in table_names:
mock_logger.info.assert_any_call(f"Successfully imported {table_name}_1.csv into {table_name}")
# Check that logger.info was called for each successful import
for table_name in table_names:
mock_logger.info.assert_any_call(f"Successfully imported {table_name}_1.csv into {table_name}")
# Check that logger.error was not called for resource class not found
mock_logger.error.assert_not_called()
# Check that logger.error was not called for resource class not found
mock_logger.error.assert_not_called()
# Check that os.remove was called for each CSV file
for table_name in table_names:
mock_remove.assert_any_call(f"{table_name}_1.csv")
# Check that os.remove was called for each CSV file
for table_name in table_names:
mock_remove.assert_any_call(f"{table_name}_1.csv")
# Check that logger.info was called for each CSV file removal
for table_name in table_names:
mock_logger.info.assert_any_call(f"Removed temporary file {table_name}_1.csv")
# Check that logger.info was called for each CSV file removal
for table_name in table_names:
mock_logger.info.assert_any_call(f"Removed temporary file {table_name}_1.csv")
@patch("registrar.management.commands.import_tables.logger")
@patch("registrar.management.commands.import_tables.os.makedirs")
@patch("registrar.management.commands.import_tables.os.path.exists")
@less_console_noise_decorator
def test_handle_zip_file_not_found(self, mock_path_exists, mock_makedirs, mock_logger):
"""Test the handle method when the zip file doesn't exist"""
with less_console_noise():
# Mock os.makedirs to do nothing
mock_makedirs.return_value = None
# Mock os.makedirs to do nothing
mock_makedirs.return_value = None
# Mock os.path.exists to return False
mock_path_exists.return_value = False
# Mock os.path.exists to return False
mock_path_exists.return_value = False
call_command("import_tables")
call_command("import_tables")
# Check that logger.error was called with the correct message
mock_logger.error.assert_called_once_with("Zip file tmp/exported_tables.zip does not exist.")
# Check that logger.error was called with the correct message
mock_logger.error.assert_called_once_with("Zip file tmp/exported_tables.zip does not exist.")
class TestTransferFederalAgencyType(TestCase):
@ -1255,6 +1255,7 @@ class TestTransferFederalAgencyType(TestCase):
id__in=[self.amtrak.id, self.legislative_branch.id, self.library_of_congress.id, self.gov_admin.id]
).delete()
@less_console_noise_decorator
def run_transfer_federal_agency_type(self):
"""
This method executes the transfer_federal_agency_type command.
@ -1262,12 +1263,11 @@ class TestTransferFederalAgencyType(TestCase):
The 'call_command' function from Django's management framework is then used to
execute the populate_first_ready command with the specified arguments.
"""
with less_console_noise():
with patch(
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa
return_value=True,
):
call_command("transfer_federal_agency_type")
with patch(
"registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", # noqa
return_value=True,
):
call_command("transfer_federal_agency_type")
@less_console_noise_decorator
def test_transfer_federal_agency_type_script(self):
@ -1630,6 +1630,7 @@ class TestCreateFederalPortfolio(TestCase):
# Test the senior official
self.assertEqual(portfolio.senior_official, self.senior_official)
@less_console_noise_decorator
def test_create_multiple_portfolios_for_branch_judicial(self):
"""Tests creating all portfolios under a given branch"""
federal_choice = DomainRequest.OrganizationChoices.FEDERAL
@ -1657,6 +1658,7 @@ class TestCreateFederalPortfolio(TestCase):
self.assertTrue(all([creator == User.get_default_user() for creator in creators]))
self.assertTrue(all([note == "Auto-generated record" for note in notes]))
@less_console_noise_decorator
def test_create_multiple_portfolios_for_branch_legislative(self):
"""Tests creating all portfolios under a given branch"""
federal_choice = DomainRequest.OrganizationChoices.FEDERAL
@ -1684,6 +1686,7 @@ class TestCreateFederalPortfolio(TestCase):
self.assertTrue(all([creator == User.get_default_user() for creator in creators]))
self.assertTrue(all([note == "Auto-generated record" for note in notes]))
@less_console_noise_decorator
def test_script_adds_requested_suborganization_information(self):
"""Tests that the script adds the requested suborg fields for domain requests"""
# Create a new domain request with some errant spacing
@ -1712,6 +1715,7 @@ class TestCreateFederalPortfolio(TestCase):
custom_suborg_request.suborganization_state_territory, DomainRequest.StateTerritoryChoices.TEXAS
)
@less_console_noise_decorator
def test_create_multiple_portfolios_for_branch_executive(self):
"""Tests creating all portfolios under a given branch"""
federal_choice = DomainRequest.OrganizationChoices.FEDERAL
@ -1774,6 +1778,7 @@ class TestCreateFederalPortfolio(TestCase):
self.assertEqual(expected_requests.count(), 2)
self.assertEqual(expected_domain_infos.count(), 2)
@less_console_noise_decorator
def test_handle_portfolio_requests(self):
"""Verify portfolio association with domain requests."""
self.run_create_federal_portfolio(agency_name="Test Federal Agency", parse_requests=True)
@ -1783,6 +1788,7 @@ class TestCreateFederalPortfolio(TestCase):
self.assertEqual(self.domain_request.portfolio.federal_agency, self.federal_agency)
self.assertEqual(self.domain_request.sub_organization.name, "Testorg")
@less_console_noise_decorator
def test_handle_portfolio_domains(self):
"""Check portfolio association with domain information."""
self.run_create_federal_portfolio(agency_name="Test Federal Agency", parse_domains=True)
@ -1792,6 +1798,7 @@ class TestCreateFederalPortfolio(TestCase):
self.assertEqual(self.domain_info.portfolio.federal_agency, self.federal_agency)
self.assertEqual(self.domain_info.sub_organization.name, "Testorg")
@less_console_noise_decorator
def test_handle_parse_both(self):
"""Ensure correct parsing of both requests and domains."""
self.run_create_federal_portfolio(agency_name="Test Federal Agency", parse_requests=True, parse_domains=True)
@ -1802,6 +1809,7 @@ class TestCreateFederalPortfolio(TestCase):
self.assertIsNotNone(self.domain_info.portfolio)
self.assertEqual(self.domain_request.portfolio, self.domain_info.portfolio)
@less_console_noise_decorator
def test_command_error_parse_options(self):
"""Verify error when bad parse options are provided."""
# The command should enforce either --branch or --agency_name
@ -1823,6 +1831,7 @@ class TestCreateFederalPortfolio(TestCase):
):
self.run_create_federal_portfolio(agency_name="test")
@less_console_noise_decorator
def test_command_error_agency_not_found(self):
"""Check error handling for non-existent agency."""
expected_message = (
@ -1832,6 +1841,7 @@ class TestCreateFederalPortfolio(TestCase):
with self.assertRaisesRegex(CommandError, expected_message):
self.run_create_federal_portfolio(agency_name="Non-existent Agency", parse_requests=True)
@less_console_noise_decorator
def test_does_not_update_existing_portfolio(self):
"""Tests that an existing portfolio is not updated when"""
# Create an existing portfolio
@ -2433,6 +2443,7 @@ class TestRemovePortfolios(TestCase):
Portfolio.objects.all().delete()
User.objects.all().delete()
@less_console_noise_decorator
@patch("registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no")
def test_delete_unlisted_portfolios(self, mock_query_yes_no):
"""Test that portfolios not on the allowed list are deleted."""
@ -2450,6 +2461,7 @@ class TestRemovePortfolios(TestCase):
self.assertFalse(Portfolio.objects.filter(organization_name="Test with suborg").exists())
self.assertTrue(Portfolio.objects.filter(organization_name="Department of Veterans Affairs").exists())
@less_console_noise_decorator
@patch("registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no")
def test_delete_entries_with_related_objects(self, mock_query_yes_no):
"""Test deletion with related objects being handled properly."""
@ -2473,6 +2485,7 @@ class TestRemovePortfolios(TestCase):
# Check that the portfolio was deleted
self.assertFalse(Portfolio.objects.filter(organization_name="Test with orphaned objects").exists())
@less_console_noise_decorator
@patch("registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no")
def test_delete_entries_with_suborganizations(self, mock_query_yes_no):
"""Test that suborganizations and their related objects are deleted along with the portfolio."""

View file

@ -2039,7 +2039,7 @@ class TestDomainRequestIncomplete(TestCase):
self.wizard = DomainRequestWizard()
self.wizard._domain_request = self.domain_request
self.wizard.request = Mock(user=self.user, session={})
self.wizard.kwargs = {"id": self.domain_request.id}
self.wizard.kwargs = {"domain_request_pk": self.domain_request.id}
# We use both of these flags in the test. In the normal app these are generated normally.
# The alternative syntax is adding the decorator to each test.

View file

@ -18,6 +18,9 @@ from .common import less_console_noise
# request on the view.
SAMPLE_KWARGS = {
"app_label": "registrar",
"domain_pk": "1",
"domain_request_pk": "1",
"domain_invitation_pk": "1",
"pk": "1",
"id": "1",
"content_type_id": "2",
@ -82,7 +85,6 @@ def iter_sample_urls(urlconf):
if not viewname:
continue
if viewname == "auth_user_password_change":
print(route)
break
named_groups = route.regex.groupindex.keys()
kwargs = {}

View file

@ -126,7 +126,7 @@ class TestEnvironmentVariablesEffects(TestCase):
with patch.object(DomainNameserversView, "get_initial", side_effect=self.side_effect_raise_value_error):
with self.assertRaises(ValueError):
contact_page_500 = self.client.get(
reverse("domain-dns-nameservers", kwargs={"pk": fake_domain.id}),
reverse("domain-dns-nameservers", kwargs={"domain_pk": fake_domain.id}),
)
# Check that a 500 response is returned
@ -147,7 +147,7 @@ class TestEnvironmentVariablesEffects(TestCase):
with patch.object(DomainNameserversView, "get_initial", side_effect=self.side_effect_raise_value_error):
with self.assertRaises(ValueError):
contact_page_500 = self.client.get(
reverse("domain-dns-nameservers", kwargs={"pk": fake_domain.id}),
reverse("domain-dns-nameservers", kwargs={"domain_pk": fake_domain.id}),
)
# Check that a 500 response is returned
@ -292,7 +292,9 @@ class HomeTests(TestWithUser):
)
# Trigger the delete logic
response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True)
response = self.client.post(
reverse("domain-request-delete", kwargs={"domain_request_pk": domain_request.pk}), follow=True
)
self.assertNotContains(response, "igorville.gov")
@ -309,7 +311,9 @@ class HomeTests(TestWithUser):
)
# Trigger the delete logic
response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True)
response = self.client.post(
reverse("domain-request-delete", kwargs={"domain_request_pk": domain_request.pk}), follow=True
)
self.assertNotContains(response, "igorville.gov")
@ -335,7 +339,8 @@ class HomeTests(TestWithUser):
# Trigger the delete logic
response = self.client.post(
reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True
reverse("domain-request-delete", kwargs={"domain_request_pk": domain_request.pk}),
follow=True,
)
# Check for a 403 error - the end user should not be allowed to do this
@ -392,7 +397,7 @@ class HomeTests(TestWithUser):
self.assertTrue(igorville.exists())
# Trigger the delete logic
self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}))
self.client.post(reverse("domain-request-delete", kwargs={"domain_request_pk": domain_request.pk}))
# igorville is now deleted
igorville = DomainRequest.objects.filter(requested_domain__name="igorville.gov")
@ -462,7 +467,7 @@ class HomeTests(TestWithUser):
self.assertTrue(teaville.exists())
# Trigger the delete logic
self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request_2.pk}))
self.client.post(reverse("domain-request-delete", kwargs={"domain_request_pk": domain_request_2.pk}))
teaville = DomainRequest.objects.filter(requested_domain__name="teaville.gov")
self.assertFalse(teaville.exists())
@ -935,7 +940,7 @@ class UserProfileTests(TestWithUser, WebTest):
@less_console_noise_decorator
def test_domain_detail_contains_your_profile(self):
"""Tests that the domain detail view contains 'your profile' rather than 'your contact information'"""
response = self.client.get(reverse("domain", args=[self.domain.pk]))
response = self.client.get(reverse("domain", kwargs={"domain_pk": self.domain.pk}))
self.assertContains(response, "Your profile")
self.assertNotContains(response, "Your contact information")

View file

@ -175,7 +175,7 @@ class TestDomainPermissions(TestWithDomainPermissions):
"domain-security-email",
]:
with self.subTest(view_name=view_name):
response = self.client.get(reverse(view_name, kwargs={"pk": self.domain.id}))
response = self.client.get(reverse(view_name, kwargs={"domain_pk": self.domain.id}))
self.assertEqual(response.status_code, 302)
@less_console_noise_decorator
@ -194,7 +194,7 @@ class TestDomainPermissions(TestWithDomainPermissions):
"domain-security-email",
]:
with self.subTest(view_name=view_name):
response = self.client.get(reverse(view_name, kwargs={"pk": self.domain.id}))
response = self.client.get(reverse(view_name, kwargs={"domain_pk": self.domain.id}))
self.assertEqual(response.status_code, 403)
@less_console_noise_decorator
@ -218,7 +218,7 @@ class TestDomainPermissions(TestWithDomainPermissions):
self.domain_deleted,
]:
with self.subTest(view_name=view_name, domain=domain):
response = self.client.get(reverse(view_name, kwargs={"pk": domain.id}))
response = self.client.get(reverse(view_name, kwargs={"domain_pk": domain.id}))
self.assertEqual(response.status_code, 403)
@ -271,20 +271,20 @@ class TestDomainDetail(TestDomainOverview):
with less_console_noise():
self.user.status = User.RESTRICTED
self.user.save()
response = self.client.get(reverse("domain", kwargs={"pk": self.domain.id}))
response = self.client.get(reverse("domain", kwargs={"domain_pk": self.domain.id}))
self.assertEqual(response.status_code, 403)
def test_domain_detail_allowed_for_on_hold(self):
"""Test that the domain overview page displays for on hold domain"""
with less_console_noise():
# View domain overview page
detail_page = self.client.get(reverse("domain", kwargs={"pk": self.domain_on_hold.id}))
detail_page = self.client.get(reverse("domain", kwargs={"domain_pk": self.domain_on_hold.id}))
self.assertNotContains(detail_page, "Edit")
def test_domain_detail_see_just_nameserver(self):
with less_console_noise():
# View nameserver on Domain Overview page
detail_page = self.app.get(reverse("domain", kwargs={"pk": self.domain_just_nameserver.id}))
detail_page = self.app.get(reverse("domain", kwargs={"domain_pk": self.domain_just_nameserver.id}))
self.assertContains(detail_page, "justnameserver.com")
self.assertContains(detail_page, "ns1.justnameserver.com")
@ -293,7 +293,7 @@ class TestDomainDetail(TestDomainOverview):
def test_domain_detail_see_nameserver_and_ip(self):
with less_console_noise():
# View nameserver on Domain Overview page
detail_page = self.app.get(reverse("domain", kwargs={"pk": self.domain_with_ip.id}))
detail_page = self.app.get(reverse("domain", kwargs={"domain_pk": self.domain_with_ip.id}))
self.assertContains(detail_page, "nameserverwithip.gov")
@ -321,7 +321,7 @@ class TestDomainDetail(TestDomainOverview):
session["analyst_action_location"] = self.domain_no_information.id
session.save()
detail_page = self.client.get(reverse("domain", kwargs={"pk": self.domain_no_information.id}))
detail_page = self.client.get(reverse("domain", kwargs={"domain_pk": self.domain_no_information.id}))
self.assertContains(detail_page, "noinformation.gov")
self.assertContains(detail_page, "Domain missing domain information")
@ -341,7 +341,7 @@ class TestDomainDetail(TestDomainOverview):
session["analyst_action_location"] = self.domain.id
session.save()
detail_page = self.client.get(reverse("domain", kwargs={"pk": self.domain.id}))
detail_page = self.client.get(reverse("domain", kwargs={"domain_pk": self.domain.id}))
self.assertNotContains(
detail_page, "If you need to make updates, contact one of the listed domain managers."
@ -486,7 +486,7 @@ class TestDomainDetailDomainRenewal(TestDomainOverview):
):
self.assertEquals(self.domain_to_renew.state, Domain.State.UNKNOWN)
detail_page = self.client.get(
reverse("domain", kwargs={"pk": self.domain_to_renew.id}),
reverse("domain", kwargs={"domain_pk": self.domain_to_renew.id}),
)
self.assertContains(detail_page, "Expiring soon")
@ -528,7 +528,7 @@ class TestDomainDetailDomainRenewal(TestDomainOverview):
Domain, "is_expired", self.custom_is_expired_false
):
detail_page = self.client.get(
reverse("domain", kwargs={"pk": domain_to_renew2.id}),
reverse("domain", kwargs={"domain_pk": domain_to_renew2.id}),
)
self.assertContains(detail_page, "Contact one of the listed domain managers to renew the domain.")
@ -548,7 +548,7 @@ class TestDomainDetailDomainRenewal(TestDomainOverview):
Domain, "is_expired", self.custom_is_expired_false
):
detail_page = self.client.get(
reverse("domain", kwargs={"pk": domain_to_renew3.id}),
reverse("domain", kwargs={"domain_pk": domain_to_renew3.id}),
)
self.assertContains(detail_page, "Renew to maintain access")
@ -561,7 +561,7 @@ class TestDomainDetailDomainRenewal(TestDomainOverview):
):
# Grab the detail page
detail_page = self.client.get(
reverse("domain", kwargs={"pk": self.domain_to_renew.id}),
reverse("domain", kwargs={"domain_pk": self.domain_to_renew.id}),
)
# Make sure we see the link as a domain manager
@ -571,7 +571,7 @@ class TestDomainDetailDomainRenewal(TestDomainOverview):
self.assertContains(detail_page, "Renewal form")
# Grab link to the renewal page
renewal_form_url = reverse("domain-renewal", kwargs={"pk": self.domain_to_renew.id})
renewal_form_url = reverse("domain-renewal", kwargs={"domain_pk": self.domain_to_renew.id})
self.assertContains(detail_page, f'href="{renewal_form_url}"')
# Simulate clicking the link
@ -590,7 +590,7 @@ class TestDomainDetailDomainRenewal(TestDomainOverview):
):
# Grab the detail page
detail_page = self.client.get(
reverse("domain", kwargs={"pk": self.domain_to_renew.id}),
reverse("domain", kwargs={"domain_pk": self.domain_to_renew.id}),
)
# Make sure we see the link as a domain manager
@ -600,7 +600,7 @@ class TestDomainDetailDomainRenewal(TestDomainOverview):
self.assertContains(detail_page, "Renewal form")
# Grab link to the renewal page
renewal_form_url = reverse("domain-renewal", kwargs={"pk": self.domain_to_renew.id})
renewal_form_url = reverse("domain-renewal", kwargs={"domain_pk": self.domain_to_renew.id})
self.assertContains(detail_page, f'href="{renewal_form_url}"')
# Simulate clicking the link
@ -614,7 +614,7 @@ class TestDomainDetailDomainRenewal(TestDomainOverview):
Your Profile portion of the Renewal Form."""
with less_console_noise():
# Start on the Renewal page for the domain
renewal_page = self.app.get(reverse("domain-renewal", kwargs={"pk": self.domain_with_ip.id}))
renewal_page = self.app.get(reverse("domain-renewal", kwargs={"domain_pk": self.domain_with_ip.id}))
# Verify we see "Your contact information" on the renewal form
self.assertContains(renewal_page, "Your contact information")
@ -633,7 +633,7 @@ class TestDomainDetailDomainRenewal(TestDomainOverview):
Security Email portion of the Renewal Form."""
with less_console_noise():
# Start on the Renewal page for the domain
renewal_page = self.app.get(reverse("domain-renewal", kwargs={"pk": self.domain_with_ip.id}))
renewal_page = self.app.get(reverse("domain-renewal", kwargs={"domain_pk": self.domain_with_ip.id}))
# Verify we see "Security email" on the renewal form
self.assertContains(renewal_page, "Security email")
@ -642,7 +642,7 @@ class TestDomainDetailDomainRenewal(TestDomainOverview):
self.assertContains(renewal_page, "We strongly recommend that you provide a security email.")
# Verify that the "Edit" button for Security email is there and links to correct URL
edit_button_url = reverse("domain-security-email", kwargs={"pk": self.domain_with_ip.id})
edit_button_url = reverse("domain-security-email", kwargs={"domain_pk": self.domain_with_ip.id})
self.assertContains(renewal_page, f'href="{edit_button_url}"')
# Simulate clicking on edit button
@ -655,13 +655,13 @@ class TestDomainDetailDomainRenewal(TestDomainOverview):
Domain Manager portion of the Renewal Form."""
with less_console_noise():
# Start on the Renewal page for the domain
renewal_page = self.app.get(reverse("domain-renewal", kwargs={"pk": self.domain_with_ip.id}))
renewal_page = self.app.get(reverse("domain-renewal", kwargs={"domain_pk": self.domain_with_ip.id}))
# Verify we see "Domain managers" on the renewal form
self.assertContains(renewal_page, "Domain managers")
# Verify that the "Edit" button for Domain managers is there and links to correct URL
edit_button_url = reverse("domain-users", kwargs={"pk": self.domain_with_ip.id})
edit_button_url = reverse("domain-users", kwargs={"domain_pk": self.domain_with_ip.id})
self.assertContains(renewal_page, f'href="{edit_button_url}"')
# Simulate clicking on edit button
@ -674,7 +674,7 @@ class TestDomainDetailDomainRenewal(TestDomainOverview):
to access /renewal and that it should receive a 403."""
with less_console_noise():
# Start on the Renewal page for the domain
renewal_page = self.client.get(reverse("domain-renewal", kwargs={"pk": self.domain_not_expiring.id}))
renewal_page = self.client.get(reverse("domain-renewal", kwargs={"domain_pk": self.domain_not_expiring.id}))
self.assertEqual(renewal_page.status_code, 403)
def test_domain_renewal_form_does_not_appear_if_not_domain_manager(self):
@ -682,13 +682,15 @@ class TestDomainDetailDomainRenewal(TestDomainOverview):
with patch.object(Domain, "is_expired", self.custom_is_expired_true), patch.object(
Domain, "is_expired", self.custom_is_expired_true
):
renewal_page = self.client.get(reverse("domain-renewal", kwargs={"pk": self.domain_no_domain_manager.id}))
renewal_page = self.client.get(
reverse("domain-renewal", kwargs={"domain_pk": self.domain_no_domain_manager.id})
)
self.assertEqual(renewal_page.status_code, 403)
def test_ack_checkbox_not_checked(self):
"""If user don't check the checkbox, user should receive an error message."""
# Grab the renewal URL
renewal_url = reverse("domain-renewal", kwargs={"pk": self.domain_with_ip.id})
renewal_url = reverse("domain-renewal", kwargs={"domain_pk": self.domain_with_ip.id})
# Test that the checkbox is not checked
response = self.client.post(renewal_url, data={"submit_button": "next"})
@ -701,17 +703,19 @@ class TestDomainDetailDomainRenewal(TestDomainOverview):
user should be redirected Domain Over page with an updated by 1 year expiration date"""
# Grab the renewal URL
with patch.object(Domain, "renew_domain", self.custom_renew_domain):
renewal_url = reverse("domain-renewal", kwargs={"pk": self.domain_with_ip.id})
renewal_url = reverse("domain-renewal", kwargs={"domain_pk": self.domain_with_ip.id})
# Click the check, and submit
response = self.client.post(renewal_url, data={"is_policy_acknowledged": "on", "submit_button": "next"})
# Check that it redirects after a successfully submits
self.assertRedirects(response, reverse("domain", kwargs={"pk": self.domain_with_ip.id}))
self.assertRedirects(response, reverse("domain", kwargs={"domain_pk": self.domain_with_ip.id}))
# Check for the updated expiration
formatted_new_expiration_date = self.expiration_date_one_year_out().strftime("%b. %-d, %Y")
redirect_response = self.client.get(reverse("domain", kwargs={"pk": self.domain_with_ip.id}), follow=True)
redirect_response = self.client.get(
reverse("domain", kwargs={"domain_pk": self.domain_with_ip.id}), follow=True
)
self.assertContains(redirect_response, formatted_new_expiration_date)
@ -754,7 +758,7 @@ class TestDomainManagers(TestDomainOverview):
@less_console_noise_decorator
def test_domain_managers(self):
response = self.client.get(reverse("domain-users", kwargs={"pk": self.domain.id}))
response = self.client.get(reverse("domain-users", kwargs={"domain_pk": self.domain.id}))
self.assertContains(response, "Domain managers")
self.assertContains(response, "Add a domain manager")
# assert that the non-portfolio view contains Role column and doesn't contain Admin
@ -765,7 +769,7 @@ class TestDomainManagers(TestDomainOverview):
@less_console_noise_decorator
@override_flag("organization_feature", active=True)
def test_domain_managers_portfolio_view(self):
response = self.client.get(reverse("domain-users", kwargs={"pk": self.domain.id}))
response = self.client.get(reverse("domain-users", kwargs={"domain_pk": self.domain.id}))
self.assertContains(response, "Domain managers")
self.assertContains(response, "Add a domain manager")
# assert that the portfolio view doesn't contain Role column and does contain Admin
@ -775,7 +779,7 @@ class TestDomainManagers(TestDomainOverview):
@less_console_noise_decorator
def test_domain_user_add(self):
response = self.client.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
response = self.client.get(reverse("domain-users-add", kwargs={"domain_pk": self.domain.id}))
self.assertContains(response, "Add a domain manager")
@less_console_noise_decorator
@ -784,7 +788,7 @@ class TestDomainManagers(TestDomainOverview):
"""Adding an existing user works."""
get_user_model().objects.get_or_create(email="mayor@igorville.gov")
user = User.objects.filter(email="mayor@igorville.gov").first()
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
add_page = self.app.get(reverse("domain-users-add", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = "mayor@igorville.gov"
@ -804,7 +808,7 @@ class TestDomainManagers(TestDomainOverview):
self.assertEqual(success_result.status_code, 302)
self.assertEqual(
success_result["Location"],
reverse("domain-users", kwargs={"pk": self.domain.id}),
reverse("domain-users", kwargs={"domain_pk": self.domain.id}),
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -820,7 +824,7 @@ class TestDomainManagers(TestDomainOverview):
"""Adding an existing user works and sends portfolio invitation when
user is not member of portfolio."""
get_user_model().objects.get_or_create(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={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = "mayor@igorville.gov"
@ -832,7 +836,7 @@ class TestDomainManagers(TestDomainOverview):
self.assertEqual(success_result.status_code, 302)
self.assertEqual(
success_result["Location"],
reverse("domain-users", kwargs={"pk": self.domain.id}),
reverse("domain-users", kwargs={"domain_pk": self.domain.id}),
)
# Verify that the invitation emails were sent
@ -877,7 +881,7 @@ class TestDomainManagers(TestDomainOverview):
self, mock_send_domain_email, mock_send_portfolio_email
):
"""Adding an email not associated with a user works and sends portfolio invitation."""
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
add_page = self.app.get(reverse("domain-users-add", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = "notauser@igorville.gov"
@ -889,7 +893,7 @@ class TestDomainManagers(TestDomainOverview):
self.assertEqual(success_result.status_code, 302)
self.assertEqual(
success_result["Location"],
reverse("domain-users", kwargs={"pk": self.domain.id}),
reverse("domain-users", kwargs={"domain_pk": self.domain.id}),
)
# Verify that the invitation emails were sent
@ -928,7 +932,7 @@ class TestDomainManagers(TestDomainOverview):
):
"""Adding an email not associated with a user works and sends portfolio invitation,
and when domain managers email(s) fail to send, assert proper warning displayed."""
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
add_page = self.app.get(reverse("domain-users-add", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = "notauser@igorville.gov"
@ -942,7 +946,7 @@ class TestDomainManagers(TestDomainOverview):
self.assertEqual(success_result.status_code, 302)
self.assertEqual(
success_result["Location"],
reverse("domain-users", kwargs={"pk": self.domain.id}),
reverse("domain-users", kwargs={"domain_pk": self.domain.id}),
)
# Verify that the invitation emails were sent
@ -967,7 +971,7 @@ class TestDomainManagers(TestDomainOverview):
UserPortfolioPermission.objects.get_or_create(
user=other_user, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
add_page = self.app.get(reverse("domain-users-add", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = "mayor@igorville.gov"
@ -979,7 +983,7 @@ class TestDomainManagers(TestDomainOverview):
self.assertEqual(success_result.status_code, 302)
self.assertEqual(
success_result["Location"],
reverse("domain-users", kwargs={"pk": self.domain.id}),
reverse("domain-users", kwargs={"domain_pk": self.domain.id}),
)
# Verify that the invitation emails were sent
@ -1015,7 +1019,7 @@ class TestDomainManagers(TestDomainOverview):
user is not member of portfolio and send raises an error."""
mock_send_portfolio_email.side_effect = EmailSendingError("Failed to send email.")
get_user_model().objects.get_or_create(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={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = "mayor@igorville.gov"
@ -1027,7 +1031,7 @@ class TestDomainManagers(TestDomainOverview):
self.assertEqual(success_result.status_code, 302)
self.assertEqual(
success_result["Location"],
reverse("domain-users", kwargs={"pk": self.domain.id}),
reverse("domain-users", kwargs={"domain_pk": self.domain.id}),
)
# Verify that the invitation emails were sent
@ -1058,7 +1062,9 @@ class TestDomainManagers(TestDomainOverview):
"""Removing a domain manager sends notification email to other domain managers."""
self.manager, _ = User.objects.get_or_create(email="mayor@igorville.com", first_name="Hello", last_name="World")
self.manager_domain_permission, _ = UserDomainRole.objects.get_or_create(user=self.manager, domain=self.domain)
self.client.post(reverse("domain-user-delete", kwargs={"pk": self.domain.id, "user_pk": self.manager.id}))
self.client.post(
reverse("domain-user-delete", kwargs={"domain_pk": self.domain.id, "user_pk": self.manager.id})
)
# Verify that the notification emails were sent to domain manager
mock_send_templated_email.assert_called_once_with(
@ -1082,7 +1088,7 @@ class TestDomainManagers(TestDomainOverview):
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
add_page = self.app.get(reverse("domain-users-add", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = email_address
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1115,7 +1121,7 @@ class TestDomainManagers(TestDomainOverview):
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
add_page = self.app.get(reverse("domain-users-add", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = caps_email_address
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1147,7 +1153,7 @@ class TestDomainManagers(TestDomainOverview):
mock_client = MagicMock()
mock_client_instance = mock_client.return_value
with boto3_mocking.clients.handler_for("sesv2", mock_client):
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
add_page = self.app.get(reverse("domain-users-add", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = email_address
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1175,7 +1181,7 @@ class TestDomainManagers(TestDomainOverview):
mock_client_instance = mock_client.return_value
with boto3_mocking.clients.handler_for("sesv2", mock_client):
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
add_page = self.app.get(reverse("domain-users-add", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = email_address
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1214,7 +1220,7 @@ class TestDomainManagers(TestDomainOverview):
mock_client_instance = mock_client.return_value
with boto3_mocking.clients.handler_for("sesv2", mock_client):
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
add_page = self.app.get(reverse("domain-users-add", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = email_address
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1258,7 +1264,7 @@ class TestDomainManagers(TestDomainOverview):
mock_client_instance = mock_client.return_value
with boto3_mocking.clients.handler_for("sesv2", mock_client):
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
add_page = self.app.get(reverse("domain-users-add", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = email_address
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1289,7 +1295,7 @@ class TestDomainManagers(TestDomainOverview):
email_address = "mayor"
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
add_page = self.app.get(reverse("domain-users-add", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = email_address
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1313,7 +1319,7 @@ class TestDomainManagers(TestDomainOverview):
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
with patch("django.contrib.messages.error") as mock_error:
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
add_page = self.app.get(reverse("domain-users-add", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = email_address
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1332,7 +1338,7 @@ class TestDomainManagers(TestDomainOverview):
"""Posting to the delete view deletes an invitation."""
email_address = "mayor@igorville.gov"
invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=email_address)
self.client.post(reverse("invitation-cancel", kwargs={"pk": invitation.id}))
self.client.post(reverse("invitation-cancel", kwargs={"domain_invitation_pk": invitation.id}))
invitation = DomainInvitation.objects.get(id=invitation.id)
self.assertEqual(invitation.status, DomainInvitation.DomainInvitationStatus.CANCELED)
@ -1343,7 +1349,9 @@ class TestDomainManagers(TestDomainOverview):
invitation, _ = DomainInvitation.objects.get_or_create(
domain=self.domain, email=email_address, status=DomainInvitation.DomainInvitationStatus.RETRIEVED
)
response = self.client.post(reverse("invitation-cancel", kwargs={"pk": invitation.id}), follow=True)
response = self.client.post(
reverse("invitation-cancel", kwargs={"domain_invitation_pk": invitation.id}), follow=True
)
# Assert that an error message is displayed to the user
self.assertContains(response, f"Invitation to {email_address} has already been retrieved.")
# Assert that the Cancel link (form) is not displayed
@ -1363,7 +1371,7 @@ class TestDomainManagers(TestDomainOverview):
self.client.force_login(other_user)
mock_client = MagicMock()
with boto3_mocking.clients.handler_for("sesv2", mock_client):
result = self.client.post(reverse("invitation-cancel", kwargs={"pk": invitation.id}))
result = self.client.post(reverse("invitation-cancel", kwargs={"domain_invitation_pk": invitation.id}))
self.assertEqual(result.status_code, 403)
@ -1380,7 +1388,7 @@ class TestDomainManagers(TestDomainOverview):
title = "title"
User.objects.filter(email=email_address).delete()
add_page = self.app.get(reverse("domain-users-add", kwargs={"pk": self.domain.id}))
add_page = self.app.get(reverse("domain-users-add", kwargs={"domain_pk": self.domain.id}))
self.domain_information, _ = DomainInformation.objects.get_or_create(creator=self.user, domain=self.domain)
@ -1418,7 +1426,7 @@ class TestDomainManagers(TestDomainOverview):
)
UserDomainRole.objects.create(user=new_user_2, domain=self.domain, role=UserDomainRole.Roles.MANAGER)
response = self.client.post(
reverse("domain-user-delete", kwargs={"pk": self.domain.id, "user_pk": new_user.id}), follow=True
reverse("domain-user-delete", kwargs={"domain_pk": self.domain.id, "user_pk": new_user.id}), follow=True
)
# Assert that a success message is displayed to the user
self.assertContains(response, f"Removed {email_address} as a manager for this domain.")
@ -1432,7 +1440,7 @@ class TestDomainManagers(TestDomainOverview):
"""Posting to the delete view attempts to delete a user domain role when there is only one manager."""
# self.user is the only domain manager, so attempt to delete it
response = self.client.post(
reverse("domain-user-delete", kwargs={"pk": self.domain.id, "user_pk": self.user.id}), follow=True
reverse("domain-user-delete", kwargs={"domain_pk": self.domain.id, "user_pk": self.user.id}), follow=True
)
# Assert that an error message is displayed to the user
self.assertContains(response, "Domains must have at least one domain manager.")
@ -1449,7 +1457,7 @@ class TestDomainManagers(TestDomainOverview):
new_user = User.objects.create(email=email_address, username="mayor")
UserDomainRole.objects.create(user=new_user, domain=self.domain, role=UserDomainRole.Roles.MANAGER)
response = self.client.post(
reverse("domain-user-delete", kwargs={"pk": self.domain.id, "user_pk": self.user.id}), follow=True
reverse("domain-user-delete", kwargs={"domain_pk": self.domain.id, "user_pk": self.user.id}), follow=True
)
# Assert that a success message is displayed to the user
self.assertContains(response, f"You are no longer managing the domain {self.domain}.")
@ -1461,7 +1469,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
@less_console_noise_decorator
def test_domain_nameservers(self):
"""Can load domain's nameservers page."""
page = self.client.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
page = self.client.get(reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}))
self.assertContains(page, "DNS name servers")
@less_console_noise_decorator
@ -1471,7 +1479,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
Uses self.app WebTest because we need to interact with forms.
"""
# initial nameservers page has one server with two ips
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# attempt to submit the form with only one nameserver, should error
@ -1494,7 +1502,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
Uses self.app WebTest because we need to interact with forms.
"""
# initial nameservers page has one server with two ips
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# attempt to submit the form without two hosts, both subdomains,
@ -1519,7 +1527,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
Uses self.app WebTest because we need to interact with forms.
"""
# initial nameservers page has one server with two ips
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# attempt to submit the form without two hosts, both subdomains,
@ -1543,7 +1551,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
Uses self.app WebTest because we need to interact with forms.
"""
# initial nameservers page has one server with two ips
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# attempt to submit the form with duplicate host names of fake.host.com
@ -1571,7 +1579,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
valid_ip = "1.1. 1.1"
valid_ip_2 = "2.2. 2.2"
# have to throw an error in order to test that the whitespace has been stripped from ip
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# attempt to submit the form without one host and an ip with whitespace
@ -1585,7 +1593,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
self.assertEqual(result.status_code, 302)
self.assertEqual(
result["Location"],
reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}),
reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}),
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
page = result.follow()
@ -1604,7 +1612,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
nameserver2 = "ns2.igorville.com"
valid_ip = "127.0.0.1"
# initial nameservers page has one server with two ips
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# attempt to submit the form without two hosts, both subdomains,
@ -1632,7 +1640,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
nameserver = "ns2.igorville.gov"
invalid_ip = "123"
# initial nameservers page has one server with two ips
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# attempt to submit the form without two hosts, both subdomains,
@ -1659,7 +1667,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
nameserver = "invalid-nameserver.gov"
valid_ip = "123.2.45.111"
# initial nameservers page has one server with two ips
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# attempt to submit the form without two hosts, both subdomains,
@ -1687,7 +1695,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
nameserver2 = "ns2.igorville.gov"
valid_ip = "127.0.0.1"
valid_ip_2 = "128.0.0.2"
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
nameservers_page.form["form-0-server"] = nameserver1
@ -1699,7 +1707,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
self.assertEqual(result.status_code, 302)
self.assertEqual(
result["Location"],
reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}),
reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}),
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
page = result.follow()
@ -1719,7 +1727,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
valid_ip = ""
valid_ip_2 = "128.0.0.2"
valid_ip_3 = "128.0.0.3"
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
nameservers_page.form["form-0-server"] = nameserver1
@ -1734,7 +1742,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
self.assertEqual(result.status_code, 302)
self.assertEqual(
result["Location"],
reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}),
reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}),
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
nameservers_page = result.follow()
@ -1760,7 +1768,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
self.assertEqual(result.status_code, 302)
self.assertEqual(
result["Location"],
reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}),
reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}),
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
nameservers_page = result.follow()
@ -1785,7 +1793,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
valid_ip_3 = ""
valid_ip_4 = ""
nameservers_page = self.app.get(
reverse("domain-dns-nameservers", kwargs={"pk": self.domain_with_four_nameservers.id})
reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain_with_four_nameservers.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
@ -1809,7 +1817,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
self.assertEqual(result.status_code, 302)
self.assertEqual(
result["Location"],
reverse("domain-dns-nameservers", kwargs={"pk": self.domain_with_four_nameservers.id}),
reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain_with_four_nameservers.id}),
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
nameservers_page = result.follow()
@ -1821,7 +1829,7 @@ class TestDomainNameservers(TestDomainOverview, MockEppLib):
Uses self.app WebTest because we need to interact with forms.
"""
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain.id}))
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# first two nameservers are required, so if we empty one out we should
@ -1843,7 +1851,7 @@ class TestDomainSeniorOfficial(TestDomainOverview):
@less_console_noise_decorator
def test_domain_senior_official(self):
"""Can load domain's senior official page."""
page = self.client.get(reverse("domain-senior-official", kwargs={"pk": self.domain.id}))
page = self.client.get(reverse("domain-senior-official", kwargs={"domain_pk": self.domain.id}))
self.assertContains(page, "Senior official", count=4)
@less_console_noise_decorator
@ -1852,7 +1860,7 @@ class TestDomainSeniorOfficial(TestDomainOverview):
self.domain_information.senior_official = Contact(first_name="Testy")
self.domain_information.senior_official.save()
self.domain_information.save()
page = self.app.get(reverse("domain-senior-official", kwargs={"pk": self.domain.id}))
page = self.app.get(reverse("domain-senior-official", kwargs={"domain_pk": self.domain.id}))
self.assertContains(page, "Testy")
@less_console_noise_decorator
@ -1864,7 +1872,7 @@ class TestDomainSeniorOfficial(TestDomainOverview):
)
self.domain_information.senior_official.save()
self.domain_information.save()
so_page = self.app.get(reverse("domain-senior-official", kwargs={"pk": self.domain.id}))
so_page = self.app.get(reverse("domain-senior-official", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
so_form = so_page.forms[0]
@ -1922,7 +1930,7 @@ class TestDomainSeniorOfficial(TestDomainOverview):
self.domain_information.senior_official.save()
self.domain_information.save()
so_page = self.app.get(reverse("domain-senior-official", kwargs={"pk": self.domain.id}))
so_page = self.app.get(reverse("domain-senior-official", kwargs={"domain_pk": self.domain.id}))
self.assertContains(so_page, "Apple Tester")
self.assertContains(so_page, "CIO")
self.assertContains(so_page, "nobody@igorville.gov")
@ -1943,7 +1951,7 @@ class TestDomainSeniorOfficial(TestDomainOverview):
self.domain_information.senior_official.save()
self.domain_information.save()
so_page = self.app.get(reverse("domain-senior-official", kwargs={"pk": self.domain.id}))
so_page = self.app.get(reverse("domain-senior-official", kwargs={"domain_pk": self.domain.id}))
self.assertContains(so_page, "Apple Tester")
self.assertContains(so_page, "CIO")
self.assertContains(so_page, "nobody@igorville.gov")
@ -1962,7 +1970,7 @@ class TestDomainSeniorOfficial(TestDomainOverview):
self.domain_information.other_contacts.add(self.domain_information.senior_official)
self.domain_information.save()
# load the Senior Official in the web form
so_page = self.app.get(reverse("domain-senior-official", kwargs={"pk": self.domain.id}))
so_page = self.app.get(reverse("domain-senior-official", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
so_form = so_page.forms[0]
@ -1990,7 +1998,7 @@ class TestDomainOrganization(TestDomainOverview):
@less_console_noise_decorator
def test_domain_org_name_address(self):
"""Can load domain's org name and mailing address page."""
page = self.client.get(reverse("domain-org-name-address", kwargs={"pk": self.domain.id}))
page = self.client.get(reverse("domain-org-name-address", kwargs={"domain_pk": self.domain.id}))
# once on the sidebar, once in the page title, once as H1
self.assertContains(page, "/org-name-address")
self.assertContains(page, "Organization name and mailing address")
@ -2001,7 +2009,7 @@ class TestDomainOrganization(TestDomainOverview):
"""Org name and address information appears on the page."""
self.domain_information.organization_name = "Town of Igorville"
self.domain_information.save()
page = self.app.get(reverse("domain-org-name-address", kwargs={"pk": self.domain.id}))
page = self.app.get(reverse("domain-org-name-address", kwargs={"domain_pk": self.domain.id}))
self.assertContains(page, "Town of Igorville")
@less_console_noise_decorator
@ -2009,7 +2017,7 @@ class TestDomainOrganization(TestDomainOverview):
"""Submitting changes works on the org name address page."""
self.domain_information.organization_name = "Town of Igorville"
self.domain_information.save()
org_name_page = self.app.get(reverse("domain-org-name-address", kwargs={"pk": self.domain.id}))
org_name_page = self.app.get(reverse("domain-org-name-address", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
org_name_page.form["organization_name"] = "Not igorville"
@ -2041,7 +2049,7 @@ class TestDomainOrganization(TestDomainOverview):
self.assertEqual(self.domain_information.generic_org_type, tribal_org_type)
org_name_page = self.app.get(reverse("domain-org-name-address", kwargs={"pk": self.domain.id}))
org_name_page = self.app.get(reverse("domain-org-name-address", kwargs={"domain_pk": self.domain.id}))
form = org_name_page.forms[0]
# Check the value of the input field
@ -2098,7 +2106,7 @@ class TestDomainOrganization(TestDomainOverview):
self.assertEqual(self.domain_information.generic_org_type, fed_org_type)
org_name_page = self.app.get(reverse("domain-org-name-address", kwargs={"pk": self.domain.id}))
org_name_page = self.app.get(reverse("domain-org-name-address", kwargs={"domain_pk": self.domain.id}))
form = org_name_page.forms[0]
# Check the value of the input field
@ -2160,7 +2168,7 @@ class TestDomainOrganization(TestDomainOverview):
new_value = ("Department of State", "Department of State")
self.client.post(
reverse("domain-org-name-address", kwargs={"pk": self.domain.id}),
reverse("domain-org-name-address", kwargs={"domain_pk": self.domain.id}),
{
"federal_agency": new_value,
},
@ -2202,7 +2210,7 @@ class TestDomainSuborganization(TestDomainOverview):
self.assertEqual(self.domain_information.sub_organization, suborg)
# Navigate to the suborganization page
page = self.app.get(reverse("domain-suborganization", kwargs={"pk": self.domain.id}))
page = self.app.get(reverse("domain-suborganization", kwargs={"domain_pk": self.domain.id}))
# The page should contain the choices Vanilla and Chocolate
self.assertContains(page, "Vanilla")
@ -2260,7 +2268,7 @@ class TestDomainSuborganization(TestDomainOverview):
self.assertEqual(self.domain_information.sub_organization, suborg)
# Navigate to the suborganization page
page = self.app.get(reverse("domain-suborganization", kwargs={"pk": self.domain.id}))
page = self.app.get(reverse("domain-suborganization", kwargs={"domain_pk": self.domain.id}))
# The page should display the readonly option
self.assertContains(page, "Vanilla")
@ -2299,7 +2307,7 @@ class TestDomainSuborganization(TestDomainOverview):
self.user.refresh_from_db()
# Navigate to the domain overview page
page = self.app.get(reverse("domain", kwargs={"pk": self.domain.id}))
page = self.app.get(reverse("domain", kwargs={"domain_pk": self.domain.id}))
# Test for the title change
self.assertContains(page, "Suborganization")
@ -2328,7 +2336,7 @@ class TestDomainSecurityEmail(TestDomainOverview):
domain_contact, _ = Domain.objects.get_or_create(name="freeman.gov")
# Add current user to this domain
_ = UserDomainRole(user=self.user, domain=domain_contact, role="admin").save()
page = self.client.get(reverse("domain-security-email", kwargs={"pk": domain_contact.id}))
page = self.client.get(reverse("domain-security-email", kwargs={"domain_pk": domain_contact.id}))
# Loads correctly
self.assertContains(page, "Security email")
@ -2343,7 +2351,7 @@ class TestDomainSecurityEmail(TestDomainOverview):
self.mockedSendFunction = self.mockSendPatch.start()
self.mockedSendFunction.side_effect = self.mockSend
page = self.client.get(reverse("domain-security-email", kwargs={"pk": self.domain.id}))
page = self.client.get(reverse("domain-security-email", kwargs={"domain_pk": self.domain.id}))
# Loads correctly
self.assertContains(page, "Security email")
@ -2353,7 +2361,7 @@ class TestDomainSecurityEmail(TestDomainOverview):
def test_domain_security_email(self):
"""Can load domain's security email page."""
with less_console_noise():
page = self.client.get(reverse("domain-security-email", kwargs={"pk": self.domain.id}))
page = self.client.get(reverse("domain-security-email", kwargs={"domain_pk": self.domain.id}))
self.assertContains(page, "Security email")
def test_domain_security_email_form(self):
@ -2361,7 +2369,7 @@ class TestDomainSecurityEmail(TestDomainOverview):
Uses self.app WebTest because we need to interact with forms.
"""
with less_console_noise():
security_email_page = self.app.get(reverse("domain-security-email", kwargs={"pk": self.domain.id}))
security_email_page = self.app.get(reverse("domain-security-email", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
security_email_page.form["security_email"] = "mayor@igorville.gov"
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -2372,7 +2380,7 @@ class TestDomainSecurityEmail(TestDomainOverview):
self.assertEqual(result.status_code, 302)
self.assertEqual(
result["Location"],
reverse("domain-security-email", kwargs={"pk": self.domain.id}),
reverse("domain-security-email", kwargs={"domain_pk": self.domain.id}),
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -2415,7 +2423,7 @@ class TestDomainSecurityEmail(TestDomainOverview):
]
for test_name, data, expected_message in test_cases:
response = self.client.post(
reverse("domain-security-email", kwargs={"pk": self.domain.id}),
reverse("domain-security-email", kwargs={"domain_pk": self.domain.id}),
data=data,
follow=True,
)
@ -2443,7 +2451,7 @@ class TestDomainSecurityEmail(TestDomainOverview):
management pages share the same permissions class"""
self.user.status = User.RESTRICTED
self.user.save()
response = self.client.get(reverse("domain", kwargs={"pk": self.domain.id}))
response = self.client.get(reverse("domain", kwargs={"domain_pk": self.domain.id}))
self.assertEqual(response.status_code, 403)
@ -2455,7 +2463,7 @@ class TestDomainDNSSEC(TestDomainOverview):
"""DNSSEC overview page loads when domain has no DNSSEC data
and shows a 'Enable DNSSEC' button."""
page = self.client.get(reverse("domain-dns-dnssec", kwargs={"pk": self.domain.id}))
page = self.client.get(reverse("domain-dns-dnssec", kwargs={"domain_pk": self.domain.id}))
self.assertContains(page, "Enable DNSSEC")
@less_console_noise_decorator
@ -2463,7 +2471,7 @@ class TestDomainDNSSEC(TestDomainOverview):
"""DNSSEC overview page loads when domain has DNSSEC data
and the template contains a button to disable DNSSEC."""
page = self.client.get(reverse("domain-dns-dnssec", kwargs={"pk": self.domain_multdsdata.id}))
page = self.client.get(reverse("domain-dns-dnssec", kwargs={"domain_pk": self.domain_multdsdata.id}))
self.assertContains(page, "Disable DNSSEC")
# Prepare the data for the POST request
@ -2471,7 +2479,7 @@ class TestDomainDNSSEC(TestDomainOverview):
"disable_dnssec": "Disable DNSSEC",
}
updated_page = self.client.post(
reverse("domain-dns-dnssec", kwargs={"pk": self.domain.id}),
reverse("domain-dns-dnssec", kwargs={"domain_pk": self.domain.id}),
post_data,
follow=True,
)
@ -2485,7 +2493,7 @@ class TestDomainDNSSEC(TestDomainOverview):
"""DNSSEC Add DS data page loads when there is no
domain DNSSEC data and shows a button to Add new record"""
page = self.client.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dnssec_none.id}))
page = self.client.get(reverse("domain-dns-dnssec-dsdata", kwargs={"domain_pk": self.domain_dnssec_none.id}))
self.assertContains(page, "You have no DS data added")
self.assertContains(page, "Add new record")
@ -2494,13 +2502,13 @@ class TestDomainDNSSEC(TestDomainOverview):
"""DNSSEC Add DS data page loads when there is
domain DNSSEC DS data and shows the data"""
page = self.client.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}))
page = self.client.get(reverse("domain-dns-dnssec-dsdata", kwargs={"domain_pk": self.domain_dsdata.id}))
self.assertContains(page, "DS data record 1")
@less_console_noise_decorator
def test_ds_data_form_modal(self):
"""When user clicks on save, a modal pops up."""
add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}))
add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"domain_pk": self.domain_dsdata.id}))
# Assert that a hidden trigger for the modal does not exist.
# 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
@ -2509,7 +2517,7 @@ class TestDomainDNSSEC(TestDomainOverview):
# Simulate a delete all data
form_data = {}
response = self.client.post(
reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}),
reverse("domain-dns-dnssec-dsdata", kwargs={"domain_pk": self.domain_dsdata.id}),
data=form_data,
)
self.assertEqual(response.status_code, 200) # Adjust status code as needed
@ -2522,7 +2530,7 @@ class TestDomainDNSSEC(TestDomainOverview):
Uses self.app WebTest because we need to interact with forms.
"""
add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}))
add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"domain_pk": self.domain_dsdata.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
result = add_data_page.forms[0].submit()
@ -2530,7 +2538,7 @@ class TestDomainDNSSEC(TestDomainOverview):
self.assertEqual(result.status_code, 302)
self.assertEqual(
result["Location"],
reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}),
reverse("domain-dns-dnssec-dsdata", kwargs={"domain_pk": self.domain_dsdata.id}),
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
page = result.follow()
@ -2542,7 +2550,7 @@ class TestDomainDNSSEC(TestDomainOverview):
Uses self.app WebTest because we need to interact with forms.
"""
add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}))
add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"domain_pk": self.domain_dsdata.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# all four form fields are required, so will test with each blank
@ -2565,7 +2573,7 @@ class TestDomainDNSSEC(TestDomainOverview):
Uses self.app WebTest because we need to interact with forms.
"""
add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}))
add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"domain_pk": self.domain_dsdata.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# first two nameservers are required, so if we empty one out we should
@ -2588,7 +2596,7 @@ class TestDomainDNSSEC(TestDomainOverview):
Uses self.app WebTest because we need to interact with forms.
"""
add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}))
add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"domain_pk": self.domain_dsdata.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# first two nameservers are required, so if we empty one out we should
@ -2611,7 +2619,7 @@ class TestDomainDNSSEC(TestDomainOverview):
Uses self.app WebTest because we need to interact with forms.
"""
add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}))
add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"domain_pk": self.domain_dsdata.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# first two nameservers are required, so if we empty one out we should
@ -2634,7 +2642,7 @@ class TestDomainDNSSEC(TestDomainOverview):
Uses self.app WebTest because we need to interact with forms.
"""
add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}))
add_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"domain_pk": self.domain_dsdata.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# first two nameservers are required, so if we empty one out we should
@ -2688,7 +2696,7 @@ class TestDomainChangeNotifications(TestDomainOverview):
self.domain_information.zipcode = "62052"
self.domain_information.save()
org_name_page = self.app.get(reverse("domain-org-name-address", kwargs={"pk": self.domain.id}))
org_name_page = self.app.get(reverse("domain-org-name-address", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
org_name_page.form["organization_name"] = "Not igorville"
@ -2729,7 +2737,7 @@ class TestDomainChangeNotifications(TestDomainOverview):
self.domain_information.portfolio = portfolio
self.domain_information.save()
org_name_page = self.app.get(reverse("domain-org-name-address", kwargs={"pk": self.domain.id}))
org_name_page = self.app.get(reverse("domain-org-name-address", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
org_name_page.form["organization_name"] = "Not igorville"
@ -2756,7 +2764,7 @@ class TestDomainChangeNotifications(TestDomainOverview):
self.domain_information.portfolio = portfolio
self.domain_information.save()
org_name_page = self.app.get(reverse("domain-org-name-address", kwargs={"pk": self.domain.id}))
org_name_page = self.app.get(reverse("domain-org-name-address", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
session = self.app.session
@ -2778,7 +2786,7 @@ class TestDomainChangeNotifications(TestDomainOverview):
def test_notification_on_security_email_change(self):
"""Test that an email is sent when the security email is changed."""
security_email_page = self.app.get(reverse("domain-security-email", kwargs={"pk": self.domain.id}))
security_email_page = self.app.get(reverse("domain-security-email", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
security_email_page.form["security_email"] = "new_security@example.com"
@ -2801,7 +2809,7 @@ class TestDomainChangeNotifications(TestDomainOverview):
def test_notification_on_dnssec_enable(self):
"""Test that an email is sent when DNSSEC is enabled."""
page = self.client.get(reverse("domain-dns-dnssec", kwargs={"pk": self.domain_multdsdata.id}))
page = self.client.get(reverse("domain-dns-dnssec", kwargs={"domain_pk": self.domain_multdsdata.id}))
self.assertContains(page, "Disable DNSSEC")
# Prepare the data for the POST request
@ -2811,7 +2819,7 @@ class TestDomainChangeNotifications(TestDomainOverview):
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
updated_page = self.client.post(
reverse("domain-dns-dnssec", kwargs={"pk": self.domain.id}),
reverse("domain-dns-dnssec", kwargs={"domain_pk": self.domain.id}),
post_data,
follow=True,
)
@ -2834,7 +2842,7 @@ class TestDomainChangeNotifications(TestDomainOverview):
def test_notification_on_ds_data_change(self):
"""Test that an email is sent when DS data is changed."""
ds_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain.id}))
ds_data_page = self.app.get(reverse("domain-dns-dnssec-dsdata", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
# Add DS data
@ -2868,7 +2876,7 @@ class TestDomainChangeNotifications(TestDomainOverview):
)
self.domain_information.save()
senior_official_page = self.app.get(reverse("domain-senior-official", kwargs={"pk": self.domain.id}))
senior_official_page = self.app.get(reverse("domain-senior-official", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
senior_official_page.form["first_name"] = "New"
@ -2905,7 +2913,7 @@ class TestDomainChangeNotifications(TestDomainOverview):
self.domain_information.portfolio = portfolio
self.domain_information.save()
senior_official_page = self.app.get(reverse("domain-senior-official", kwargs={"pk": self.domain.id}))
senior_official_page = self.app.get(reverse("domain-senior-official", kwargs={"domain_pk": self.domain.id}))
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
senior_official_page.form["first_name"] = "New"
@ -2924,7 +2932,9 @@ class TestDomainChangeNotifications(TestDomainOverview):
def test_no_notification_when_dns_needed(self):
"""Test that an email is not sent when nameservers are changed while the state is DNS_NEEDED."""
nameservers_page = self.app.get(reverse("domain-dns-nameservers", kwargs={"pk": self.domain_dns_needed.id}))
nameservers_page = self.app.get(
reverse("domain-dns-nameservers", kwargs={"domain_pk": self.domain_dns_needed.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
# add nameservers

View file

@ -104,7 +104,7 @@ class GetDomainsJsonTest(TestWithUser, WebTest):
self.assertEqual(expected_domain.state_display(), state_displays[i])
self.assertEqual(expected_domain.get_state_help_text(), get_state_help_texts[i])
self.assertEqual(reverse("domain", kwargs={"pk": expected_domain.id}), action_urls[i])
self.assertEqual(reverse("domain", kwargs={"domain_pk": expected_domain.id}), action_urls[i])
# Check action_label
action_label_expected = (
@ -185,7 +185,7 @@ class GetDomainsJsonTest(TestWithUser, WebTest):
self.assertEqual(expected_domain.state_display(), state_displays[i])
self.assertEqual(expected_domain.get_state_help_text(), get_state_help_texts[i])
self.assertEqual(reverse("domain", kwargs={"pk": expected_domain.id}), action_urls[i])
self.assertEqual(reverse("domain", kwargs={"domain_pk": expected_domain.id}), action_urls[i])
# Check action_label
user_domain_role_exists = UserDomainRole.objects.filter(
@ -272,7 +272,7 @@ class GetDomainsJsonTest(TestWithUser, WebTest):
self.assertEqual(expected_domain.state_display(), state_displays[i])
self.assertEqual(expected_domain.get_state_help_text(), get_state_help_texts[i])
self.assertEqual(reverse("domain", kwargs={"pk": expected_domain.id}), action_urls[i])
self.assertEqual(reverse("domain", kwargs={"domain_pk": expected_domain.id}), action_urls[i])
# Check action_label
user_domain_role_exists = UserDomainRole.objects.filter(

View file

@ -1320,7 +1320,9 @@ class TestPortfolio(WebTest):
self.client.force_login(self.user)
# Perform delete
response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True)
response = self.client.post(
reverse("domain-request-delete", kwargs={"domain_request_pk": domain_request.pk}), follow=True
)
# Check that the response is 200
self.assertEqual(response.status_code, 200)
@ -1354,7 +1356,9 @@ class TestPortfolio(WebTest):
self.client.force_login(self.user)
# Attempt to delete
response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True)
response = self.client.post(
reverse("domain-request-delete", kwargs={"domain_request_pk": domain_request.pk}), follow=True
)
# Check response is 403 Forbidden
self.assertEqual(response.status_code, 403)
@ -1389,7 +1393,9 @@ class TestPortfolio(WebTest):
self.client.force_login(self.user)
# Perform delete as self.user
response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True)
response = self.client.post(
reverse("domain-request-delete", kwargs={"domain_request_pk": domain_request.pk}), follow=True
)
# Check response is 403 Forbidden
self.assertEqual(response.status_code, 403)
@ -3244,7 +3250,9 @@ class TestRequestingEntity(WebTest):
def test_requesting_entity_page_errors(self):
"""Tests that we get the expected form errors on requesting entity"""
domain_request = completed_domain_request(user=self.user, portfolio=self.portfolio)
response = self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk})).follow()
response = self.app.get(
reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk})
).follow()
form = response.forms[0]
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -3334,7 +3342,9 @@ class TestRequestingEntity(WebTest):
domain_request.submit()
response = self.app.get(reverse("domain-request-status-viewonly", kwargs={"pk": domain_request.pk}))
response = self.app.get(
reverse("domain-request-status-viewonly", kwargs={"domain_request_pk": domain_request.pk})
)
self.assertContains(response, "Requesting entity")
self.assertContains(response, "moon")
self.assertContains(response, "kepler, AL")
@ -3359,7 +3369,7 @@ class TestRequestingEntity(WebTest):
domain_request.submit()
response = self.app.get(reverse("domain-request-status", kwargs={"pk": domain_request.pk}))
response = self.app.get(reverse("domain-request-status", kwargs={"domain_request_pk": domain_request.pk}))
self.assertContains(response, "Requesting entity")
self.assertContains(response, "moon")
self.assertContains(response, "kepler, AL")

View file

@ -809,7 +809,7 @@ class DomainRequestTests(TestWithUser, WebTest):
# type_page = home_page.click("Edit")
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
url = reverse("edit-domain-request", kwargs={"id": domain_request.pk})
url = reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk})
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# TODO: The following line results in a django error on middleware
@ -1106,7 +1106,7 @@ class DomainRequestTests(TestWithUser, WebTest):
def test_yes_no_contact_form_inits_blank_for_new_domain_request(self):
"""On the Other Contacts page, the yes/no form gets initialized with nothing selected for
new domain requests"""
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": 0}))
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"domain_request_pk": 0}))
other_contacts_form = other_contacts_page.forms[0]
self.assertEquals(other_contacts_form["other_contacts-has_other_contacts"].value, None)
@ -1114,7 +1114,9 @@ class DomainRequestTests(TestWithUser, WebTest):
def test_yes_no_additional_form_inits_blank_for_new_domain_request(self):
"""On the Additional Details page, the yes/no form gets initialized with nothing selected for
new domain requests"""
additional_details_page = self.app.get(reverse("domain-request:additional_details", kwargs={"id": 0}))
additional_details_page = self.app.get(
reverse("domain-request:additional_details", kwargs={"domain_request_pk": 0})
)
additional_form = additional_details_page.forms[0]
# Check the cisa representative yes/no field
@ -1130,7 +1132,7 @@ class DomainRequestTests(TestWithUser, WebTest):
# Domain Request has other contacts by default
domain_request = completed_domain_request(user=self.user)
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1138,7 +1140,9 @@ class DomainRequestTests(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.pk}))
other_contacts_page = self.app.get(
reverse("domain-request:other_contacts", kwargs={"domain_request_pk": domain_request.pk})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_form = other_contacts_page.forms[0]
@ -1155,7 +1159,7 @@ class DomainRequestTests(TestWithUser, WebTest):
domain_request.save()
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1164,7 +1168,7 @@ class DomainRequestTests(TestWithUser, WebTest):
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
additional_details_page = self.app.get(
reverse("domain-request:additional_details", kwargs={"id": domain_request.pk})
reverse("domain-request:additional_details", kwargs={"domain_request_pk": domain_request.pk})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1187,7 +1191,7 @@ class DomainRequestTests(TestWithUser, WebTest):
domain_request.no_other_contacts_rationale = "Hello!"
domain_request.save()
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1195,7 +1199,9 @@ class DomainRequestTests(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.pk}))
other_contacts_page = self.app.get(
reverse("domain-request:other_contacts", kwargs={"domain_request_pk": domain_request.pk})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_form = other_contacts_page.forms[0]
@ -1216,7 +1222,7 @@ class DomainRequestTests(TestWithUser, WebTest):
domain_request.save()
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1225,7 +1231,7 @@ class DomainRequestTests(TestWithUser, WebTest):
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
additional_details_page = self.app.get(
reverse("domain-request:additional_details", kwargs={"id": domain_request.pk})
reverse("domain-request:additional_details", kwargs={"domain_request_pk": domain_request.pk})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1256,7 +1262,7 @@ class DomainRequestTests(TestWithUser, WebTest):
self.assertEqual(domain_request.cisa_representative_email, "fake@faketown.gov")
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1265,7 +1271,7 @@ class DomainRequestTests(TestWithUser, WebTest):
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
additional_details_page = self.app.get(
reverse("domain-request:additional_details", kwargs={"id": domain_request.pk})
reverse("domain-request:additional_details", kwargs={"domain_request_pk": domain_request.pk})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1320,7 +1326,7 @@ class DomainRequestTests(TestWithUser, WebTest):
self.assertEqual(domain_request.has_cisa_representative, None)
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1329,7 +1335,7 @@ class DomainRequestTests(TestWithUser, WebTest):
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
additional_details_page = self.app.get(
reverse("domain-request:additional_details", kwargs={"id": domain_request.pk})
reverse("domain-request:additional_details", kwargs={"domain_request_pk": domain_request.pk})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1367,7 +1373,7 @@ class DomainRequestTests(TestWithUser, WebTest):
)
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1376,7 +1382,7 @@ class DomainRequestTests(TestWithUser, WebTest):
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
additional_details_page = self.app.get(
reverse("domain-request:additional_details", kwargs={"id": domain_request.pk})
reverse("domain-request:additional_details", kwargs={"domain_request_pk": domain_request.pk})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1400,7 +1406,7 @@ class DomainRequestTests(TestWithUser, WebTest):
domain_request = completed_domain_request(name="cisareps.gov", user=self.user, has_anything_else=False)
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1409,7 +1415,7 @@ class DomainRequestTests(TestWithUser, WebTest):
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
additional_details_page = self.app.get(
reverse("domain-request:additional_details", kwargs={"id": domain_request.pk})
reverse("domain-request:additional_details", kwargs={"domain_request_pk": domain_request.pk})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1439,7 +1445,7 @@ class DomainRequestTests(TestWithUser, WebTest):
self.assertEqual(domain_request.has_cisa_representative, None)
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1448,7 +1454,7 @@ class DomainRequestTests(TestWithUser, WebTest):
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
additional_details_page = self.app.get(
reverse("domain-request:additional_details", kwargs={"id": domain_request.id})
reverse("domain-request:additional_details", kwargs={"domain_request_pk": domain_request.id})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@ -1472,7 +1478,7 @@ class DomainRequestTests(TestWithUser, WebTest):
domain_request.no_other_contacts_rationale = "Hello!"
domain_request.save()
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1480,7 +1486,9 @@ class DomainRequestTests(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.pk}))
other_contacts_page = self.app.get(
reverse("domain-request:other_contacts", kwargs={"domain_request_pk": domain_request.pk})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_form = other_contacts_page.forms[0]
@ -1520,7 +1528,7 @@ class DomainRequestTests(TestWithUser, WebTest):
# Domain request has other contacts by default
domain_request = completed_domain_request(user=self.user)
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1528,7 +1536,9 @@ class DomainRequestTests(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.pk}))
other_contacts_page = self.app.get(
reverse("domain-request:other_contacts", kwargs={"domain_request_pk": domain_request.pk})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_form = other_contacts_page.forms[0]
@ -1604,7 +1614,7 @@ class DomainRequestTests(TestWithUser, WebTest):
domain_info.other_contacts.set([other])
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1612,7 +1622,9 @@ class DomainRequestTests(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.pk}))
other_contacts_page = self.app.get(
reverse("domain-request:other_contacts", kwargs={"domain_request_pk": domain_request.pk})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_form = other_contacts_page.forms[0]
@ -1653,7 +1665,7 @@ class DomainRequestTests(TestWithUser, WebTest):
@less_console_noise_decorator
def test_if_yes_no_form_is_no_then_no_other_contacts_required(self):
"""Applicants with no other contacts have to give a reason."""
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": 0}))
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"domain_request_pk": 0}))
other_contacts_form = other_contacts_page.forms[0]
other_contacts_form["other_contacts-has_other_contacts"] = "False"
response = other_contacts_page.forms[0].submit()
@ -1669,7 +1681,7 @@ class DomainRequestTests(TestWithUser, WebTest):
@less_console_noise_decorator
def test_if_yes_no_form_is_yes_then_other_contacts_required(self):
"""Applicants with other contacts do not have to give a reason."""
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": 0}))
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"domain_request_pk": 0}))
other_contacts_form = other_contacts_page.forms[0]
other_contacts_form["other_contacts-has_other_contacts"] = "True"
response = other_contacts_page.forms[0].submit()
@ -1737,7 +1749,7 @@ class DomainRequestTests(TestWithUser, WebTest):
domain_request.other_contacts.add(other2)
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1745,7 +1757,9 @@ class DomainRequestTests(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.id}))
other_contacts_page = self.app.get(
reverse("domain-request:other_contacts", kwargs={"domain_request_pk": domain_request.id})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_form = other_contacts_page.forms[0]
@ -1810,7 +1824,7 @@ class DomainRequestTests(TestWithUser, WebTest):
domain_request.other_contacts.add(other)
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1818,7 +1832,9 @@ class DomainRequestTests(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.id}))
other_contacts_page = self.app.get(
reverse("domain-request:other_contacts", kwargs={"domain_request_pk": domain_request.id})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_form = other_contacts_page.forms[0]
@ -1887,7 +1903,7 @@ class DomainRequestTests(TestWithUser, WebTest):
domain_request.other_contacts.add(other)
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1895,7 +1911,9 @@ class DomainRequestTests(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.id}))
other_contacts_page = self.app.get(
reverse("domain-request:other_contacts", kwargs={"domain_request_pk": domain_request.id})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_form = other_contacts_page.forms[0]
@ -1967,7 +1985,7 @@ class DomainRequestTests(TestWithUser, WebTest):
other_contact_pk = other.id
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -1975,7 +1993,9 @@ class DomainRequestTests(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.pk}))
other_contacts_page = self.app.get(
reverse("domain-request:other_contacts", kwargs={"domain_request_pk": domain_request.pk})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_form = other_contacts_page.forms[0]
@ -2043,7 +2063,7 @@ class DomainRequestTests(TestWithUser, WebTest):
other_contact_pk = so.id
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -2051,7 +2071,9 @@ class DomainRequestTests(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_page = self.app.get(reverse("domain-request:other_contacts", kwargs={"id": domain_request.pk}))
other_contacts_page = self.app.get(
reverse("domain-request:other_contacts", kwargs={"domain_request_pk": domain_request.pk})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
other_contacts_form = other_contacts_page.forms[0]
@ -2113,7 +2135,7 @@ class DomainRequestTests(TestWithUser, WebTest):
so_pk = so.id
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -2121,7 +2143,9 @@ class DomainRequestTests(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
so_page = self.app.get(reverse("domain-request:senior_official", kwargs={"id": domain_request.pk}))
so_page = self.app.get(
reverse("domain-request:senior_official", kwargs={"domain_request_pk": domain_request.pk})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
so_form = so_page.forms[0]
@ -2182,7 +2206,7 @@ class DomainRequestTests(TestWithUser, WebTest):
so_pk = so.id
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -2190,7 +2214,9 @@ class DomainRequestTests(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
so_page = self.app.get(reverse("domain-request:senior_official", kwargs={"id": domain_request.pk}))
so_page = self.app.get(
reverse("domain-request:senior_official", kwargs={"domain_request_pk": domain_request.pk})
)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
so_form = so_page.forms[0]
@ -2240,7 +2266,7 @@ class DomainRequestTests(TestWithUser, WebTest):
creator_pk = self.user.id
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
self.app.get(reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}))
# django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here
@ -2539,7 +2565,9 @@ class DomainRequestTests(TestWithUser, WebTest):
new_domain_request_id = all_domain_requests.first().id
# Skip to the current sites page
current_sites_page = self.app.get(reverse("domain-request:current_sites", kwargs={"id": new_domain_request_id}))
current_sites_page = self.app.get(
reverse("domain-request:current_sites", kwargs={"domain_request_pk": new_domain_request_id})
)
# fill in the form field
current_sites_form = current_sites_page.forms[0]
self.assertIn("current_sites-0-website", current_sites_form.fields)
@ -2614,7 +2642,7 @@ class DomainRequestTests(TestWithUser, WebTest):
domain_request.alternative_domains.add(alt)
# prime the form by visiting /edit
url = reverse("edit-domain-request", kwargs={"id": domain_request.pk})
url = reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk})
response = self.client.get(url)
# TODO: this is a sketch of each page in the wizard which needs to be tested
@ -2724,7 +2752,7 @@ class DomainRequestTests(TestWithUser, WebTest):
NOTE: This may be a moot point if we implement a more solid pattern in the
future, like not a submit action at all on the review page."""
review_page = self.app.get(reverse("domain-request:review", kwargs={"id": 0}))
review_page = self.app.get(reverse("domain-request:review", kwargs={"domain_request_pk": 0}))
self.assertContains(review_page, "toggle-submit-domain-request")
self.assertContains(review_page, "Your request form is incomplete")
@ -2742,7 +2770,9 @@ class DomainRequestTests(TestWithUser, WebTest):
# This user should also be forbidden from editing existing ones
domain_request = completed_domain_request(user=self.user)
edit_page = self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}), expect_errors=True)
edit_page = self.app.get(
reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}), expect_errors=True
)
self.assertEqual(edit_page.status_code, 403)
# Cleanup
@ -2767,7 +2797,9 @@ class DomainRequestTests(TestWithUser, WebTest):
# This user should also be allowed to edit existing ones
domain_request = completed_domain_request(user=self.user)
edit_page = self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk})).follow()
edit_page = self.app.get(
reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk})
).follow()
self.assertEqual(edit_page.status_code, 200)
def test_non_creator_access(self):
@ -2776,14 +2808,18 @@ class DomainRequestTests(TestWithUser, WebTest):
other_user = User.objects.create_user(username="other_user", password=p)
domain_request = completed_domain_request(user=other_user)
edit_page = self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}), expect_errors=True)
edit_page = self.app.get(
reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk}), expect_errors=True
)
self.assertEqual(edit_page.status_code, 403)
def test_creator_access(self):
"""Tests that a user can edit a domain request they created"""
domain_request = completed_domain_request(user=self.user)
edit_page = self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk})).follow()
edit_page = self.app.get(
reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.pk})
).follow()
self.assertEqual(edit_page.status_code, 200)
@ -2898,12 +2934,8 @@ class DomainRequestTestDifferentStatuses(TestWithUser, WebTest):
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user)
domain_request.save()
detail_page = self.app.get(f"/domain-request/{domain_request.id}")
self.assertContains(detail_page, "city.gov")
self.assertContains(detail_page, "city1.gov")
self.assertContains(detail_page, "Chief Tester")
self.assertContains(detail_page, "testy@town.com")
self.assertContains(detail_page, "Status:")
detail_page = self.client.get(f"/domain-request/{domain_request.id}")
self.assertEqual(detail_page.status_code, 403)
# Restricted user trying to withdraw results in 403 error
with less_console_noise():
for url_name in [
@ -2911,7 +2943,7 @@ class DomainRequestTestDifferentStatuses(TestWithUser, WebTest):
"domain-request-withdrawn",
]:
with self.subTest(url_name=url_name):
page = self.client.get(reverse(url_name, kwargs={"pk": domain_request.pk}))
page = self.client.get(reverse(url_name, kwargs={"domain_request_pk": domain_request.pk}))
self.assertEqual(page.status_code, 403)
@less_console_noise_decorator
@ -2931,7 +2963,7 @@ class DomainRequestTestDifferentStatuses(TestWithUser, WebTest):
"domain-request-withdrawn",
]:
with self.subTest(url_name=url_name):
page = self.client.get(reverse(url_name, kwargs={"pk": domain_request.pk}))
page = self.client.get(reverse(url_name, kwargs={"domain_request_pk": domain_request.pk}))
self.assertEqual(page.status_code, 403)
@less_console_noise_decorator
@ -3206,7 +3238,9 @@ class TestDomainRequestWizard(TestWithUser, WebTest):
self.assertContains(detail_page, "usa-current", count=2)
# We default to the requesting entity page
expected_url = reverse("domain-request:portfolio_requesting_entity", kwargs={"id": domain_request.id})
expected_url = reverse(
"domain-request:portfolio_requesting_entity", kwargs={"domain_request_pk": domain_request.id}
)
# This returns the entire url, thus "in"
self.assertIn(expected_url, detail_page.request.url)
else:

View file

@ -199,14 +199,14 @@ class GetRequestsJsonTest(TestWithUser, WebTest):
# Check action_url
action_url_expected = (
reverse("edit-domain-request", kwargs={"id": self.domain_requests[i].id})
reverse("edit-domain-request", kwargs={"domain_request_pk": self.domain_requests[i].id})
if self.domain_requests[i].status
in [
DomainRequest.DomainRequestStatus.STARTED,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
DomainRequest.DomainRequestStatus.WITHDRAWN,
]
else reverse("domain-request-status", kwargs={"pk": self.domain_requests[i].id})
else reverse("domain-request-status", kwargs={"domain_request_pk": self.domain_requests[i].id})
)
self.assertEqual(action_url_expected, action_urls[i])
@ -342,7 +342,8 @@ class GetRequestsJsonTest(TestWithUser, WebTest):
# Test case where action is View
self.assertEqual("View", action_labels[i])
self.assertEqual(
reverse("domain-request-status-viewonly", kwargs={"pk": expected_domain_request.id}), action_urls[i]
reverse("domain-request-status-viewonly", kwargs={"domain_request_pk": expected_domain_request.id}),
action_urls[i],
)
self.assertEqual("visibility", svg_icons[i])
elif status[i] in [
@ -360,7 +361,8 @@ class GetRequestsJsonTest(TestWithUser, WebTest):
# Test case where action is Manage
self.assertEqual("Manage", action_labels[i])
self.assertEqual(
reverse("domain-request-status", kwargs={"pk": expected_domain_request.id}), action_urls[i]
reverse("domain-request-status", kwargs={"domain_request_pk": expected_domain_request.id}),
action_urls[i],
)
self.assertEqual("settings", svg_icons[i])

View file

@ -1,20 +1,23 @@
"""Views for a single Domain.
Authorization is handled by the `DomainPermissionView`. To ensure that only
authorized users can see information on a domain, every view here should
inherit from `DomainPermissionView` (or DomainInvitationPermissionCancelView).
"""
from datetime import date
import logging
import requests
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.http import HttpResponseRedirect
from django.shortcuts import redirect, render, get_object_or_404
from django.urls import reverse
from django.views.generic import DeleteView, DetailView, UpdateView
from django.views.generic.edit import FormMixin
from django.conf import settings
from registrar.decorators import (
HAS_PORTFOLIO_DOMAINS_VIEW_ALL,
IS_DOMAIN_MANAGER,
IS_DOMAIN_MANAGER_AND_NOT_PORTFOLIO_MEMBER,
IS_PORTFOLIO_MEMBER_AND_DOMAIN_MANAGER,
IS_STAFF_MANAGING_DOMAIN,
grant_access,
)
from registrar.forms.domain import DomainSuborganizationForm, DomainRenewalForm
from registrar.models import (
Domain,
@ -40,7 +43,6 @@ from registrar.utility.errors import (
SecurityEmailErrorCodes,
)
from registrar.models.utility.contact_error import ContactError
from registrar.views.utility.permission_views import UserDomainRolePermissionDeleteView
from registrar.utility.waffle import flag_is_active_for_user
from registrar.views.utility.invitation_helper import (
get_org_membership,
@ -67,19 +69,22 @@ from epplibwrapper import (
from ..utility.email import send_templated_email, EmailSendingError
from ..utility.email_invitations import send_domain_invitation_email, send_portfolio_invitation_email
from .utility import DomainPermissionView, DomainInvitationPermissionCancelView
from django import forms
logger = logging.getLogger(__name__)
class DomainBaseView(DomainPermissionView):
class DomainBaseView(PermissionRequiredMixin, DetailView):
"""
Base View for the Domain. Handles getting and setting the domain
in session cache on GETs. Also provides methods for getting
and setting the domain in cache
"""
model = Domain
pk_url_kwarg = "domain_pk"
context_object_name = "domain"
def get(self, request, *args, **kwargs):
self._get_domain(request)
context = self.get_context_data(object=self.object)
@ -95,7 +100,7 @@ class DomainBaseView(DomainPermissionView):
self.session = request.session
# domain:private_key is the session key to use for
# caching the domain in the session
domain_pk = "domain:" + str(self.kwargs.get("pk"))
domain_pk = "domain:" + str(self.kwargs.get("domain_pk"))
cached_domain = self.session.get(domain_pk)
if cached_domain:
@ -108,9 +113,136 @@ class DomainBaseView(DomainPermissionView):
"""
update domain in the session cache
"""
domain_pk = "domain:" + str(self.kwargs.get("pk"))
domain_pk = "domain:" + str(self.kwargs.get("domain_pk"))
self.session[domain_pk] = self.object
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user = self.request.user
context["is_analyst_or_superuser"] = user.has_perm("registrar.analyst_access_permission") or user.has_perm(
"registrar.full_access_permission"
)
context["is_domain_manager"] = UserDomainRole.objects.filter(user=user, domain=self.object).exists()
context["is_portfolio_user"] = self.can_access_domain_via_portfolio(self.object.pk)
context["is_editable"] = self.is_editable()
# Stored in a variable for the linter
action = "analyst_action"
action_location = "analyst_action_location"
# Flag to see if an analyst is attempting to make edits
if action in self.request.session:
context[action] = self.request.session[action]
if action_location in self.request.session:
context[action_location] = self.request.session[action_location]
return context
def is_editable(self):
"""Returns whether domain is editable in the context of the view"""
domain_editable = self.object.is_editable()
if not domain_editable:
return False
# if user is domain manager or analyst or admin, return True
if (
self.can_access_other_user_domains(self.object.id)
or UserDomainRole.objects.filter(user=self.request.user, domain=self.object).exists()
):
return True
return False
def can_access_domain_via_portfolio(self, pk):
"""Most views should not allow permission to portfolio users.
If particular views allow access to the domain pages, they will need to override
this function.
"""
return False
def has_permission(self):
"""Check if this user has access to this domain.
The user is in self.request.user and the domain needs to be looked
up from the domain's primary key in self.kwargs["domain_pk"]
"""
pk = self.kwargs["domain_pk"]
# test if domain in editable state
if not self.in_editable_state(pk):
return False
# if we need to check more about the nature of role, do it here.
return True
def in_editable_state(self, pk):
"""Is the domain in an editable state"""
requested_domain = None
if Domain.objects.filter(id=pk).exists():
requested_domain = Domain.objects.get(id=pk)
# if domain is editable return true
if requested_domain and requested_domain.is_editable():
return True
return False
def can_access_other_user_domains(self, pk):
"""Checks to see if an authorized user (staff or superuser)
can access a domain that they did not create or was invited to.
"""
# Check if the user is permissioned...
user_is_analyst_or_superuser = self.request.user.has_perm(
"registrar.analyst_access_permission"
) or self.request.user.has_perm("registrar.full_access_permission")
if not user_is_analyst_or_superuser:
return False
# Check if the user is attempting a valid edit action.
# In other words, if the analyst/admin did not click
# the 'Manage Domain' button in /admin,
# then they cannot access this page.
session = self.request.session
can_do_action = (
"analyst_action" in session
and "analyst_action_location" in session
and session["analyst_action_location"] == pk
)
if not can_do_action:
return False
# Analysts may manage domains, when they are in these statuses:
valid_domain_statuses = [
DomainRequest.DomainRequestStatus.APPROVED,
DomainRequest.DomainRequestStatus.IN_REVIEW,
DomainRequest.DomainRequestStatus.REJECTED,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
# Edge case - some domains do not have
# a status or DomainInformation... aka a status of 'None'.
# It is necessary to access those to correct errors.
None,
]
requested_domain = None
if DomainInformation.objects.filter(id=pk).exists():
requested_domain = DomainInformation.objects.get(id=pk)
# if no domain information or domain request exist, the user
# should be able to manage the domain; however, if domain information
# and domain request exist, and domain request is not in valid status,
# user should not be able to manage domain
if (
requested_domain
and requested_domain.domain_request
and requested_domain.domain_request.status not in valid_domain_statuses
):
return False
# Valid session keys exist,
# the user is permissioned,
# and it is in a valid status
return True
class DomainFormBaseView(DomainBaseView, FormMixin):
"""
@ -257,6 +389,7 @@ class DomainFormBaseView(DomainBaseView, FormMixin):
)
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN, HAS_PORTFOLIO_DOMAINS_VIEW_ALL)
class DomainView(DomainBaseView):
"""Domain detail overview page."""
@ -311,6 +444,7 @@ class DomainView(DomainBaseView):
self._update_session_with_domain()
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainRenewalView(DomainBaseView):
"""Domain detail overview page."""
@ -344,9 +478,9 @@ class DomainRenewalView(DomainBaseView):
and (requested_domain.is_expiring() or requested_domain.is_expired())
)
def post(self, request, pk):
def post(self, request, domain_pk):
domain = get_object_or_404(Domain, id=pk)
domain = get_object_or_404(Domain, id=domain_pk)
form = DomainRenewalForm(request.POST)
@ -363,7 +497,7 @@ class DomainRenewalView(DomainBaseView):
"This domain has not been renewed for one year, "
"please email help@get.gov if this problem persists.",
)
return HttpResponseRedirect(reverse("domain", kwargs={"pk": pk}))
return HttpResponseRedirect(reverse("domain", kwargs={"domain_pk": domain_pk}))
# if not valid, render the template with error messages
# passing editable and is_editable for re-render
@ -379,6 +513,7 @@ class DomainRenewalView(DomainBaseView):
)
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainOrgNameAddressView(DomainFormBaseView):
"""Organization view"""
@ -395,7 +530,7 @@ class DomainOrgNameAddressView(DomainFormBaseView):
def get_success_url(self):
"""Redirect to the overview page for the domain."""
return reverse("domain-org-name-address", kwargs={"pk": self.object.pk})
return reverse("domain-org-name-address", kwargs={"domain_pk": self.object.pk})
def form_valid(self, form):
"""The form is valid, save the organization name and mailing address."""
@ -420,6 +555,7 @@ class DomainOrgNameAddressView(DomainFormBaseView):
return super().has_permission()
@grant_access(IS_PORTFOLIO_MEMBER_AND_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainSubOrganizationView(DomainFormBaseView):
"""Suborganization view"""
@ -454,7 +590,7 @@ class DomainSubOrganizationView(DomainFormBaseView):
def get_success_url(self):
"""Redirect to the overview page for the domain."""
return reverse("domain-suborganization", kwargs={"pk": self.object.pk})
return reverse("domain-suborganization", kwargs={"domain_pk": self.object.pk})
def form_valid(self, form):
"""The form is valid, save the organization name and mailing address."""
@ -466,6 +602,7 @@ class DomainSubOrganizationView(DomainFormBaseView):
return super().form_valid(form)
@grant_access(IS_DOMAIN_MANAGER_AND_NOT_PORTFOLIO_MEMBER, IS_STAFF_MANAGING_DOMAIN)
class DomainSeniorOfficialView(DomainFormBaseView):
"""Domain senior official editing view."""
@ -493,7 +630,7 @@ class DomainSeniorOfficialView(DomainFormBaseView):
def get_success_url(self):
"""Redirect to the overview page for the domain."""
return reverse("domain-senior-official", kwargs={"pk": self.object.pk})
return reverse("domain-senior-official", kwargs={"domain_pk": self.object.pk})
def form_valid(self, form):
"""The form is valid, save the senior official."""
@ -523,6 +660,7 @@ class DomainSeniorOfficialView(DomainFormBaseView):
return super().has_permission()
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainDNSView(DomainBaseView):
"""DNS Information View."""
@ -586,7 +724,7 @@ class PrototypeDomainDNSRecordView(DomainFormBaseView):
return True
def get_success_url(self):
return reverse("prototype-domain-dns", kwargs={"pk": self.object.pk})
return reverse("prototype-domain-dns", kwargs={"domain_pk": self.object.pk})
def find_by_name(self, items, name):
"""Find an item by name in a list of dictionaries."""
@ -739,6 +877,7 @@ class PrototypeDomainDNSRecordView(DomainFormBaseView):
return super().post(request)
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainNameserversView(DomainFormBaseView):
"""Domain nameserver editing view."""
@ -763,7 +902,7 @@ class DomainNameserversView(DomainFormBaseView):
def get_success_url(self):
"""Redirect to the nameservers page for the domain."""
return reverse("domain-dns-nameservers", kwargs={"pk": self.object.pk})
return reverse("domain-dns-nameservers", kwargs={"domain_pk": self.object.pk})
def get_context_data(self, **kwargs):
"""Adjust context from FormMixin for formsets."""
@ -866,6 +1005,7 @@ class DomainNameserversView(DomainFormBaseView):
return super().form_valid(formset)
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainDNSSECView(DomainFormBaseView):
"""Domain DNSSEC editing view."""
@ -884,7 +1024,7 @@ class DomainDNSSECView(DomainFormBaseView):
def get_success_url(self):
"""Redirect to the DNSSEC page for the domain."""
return reverse("domain-dns-dnssec", kwargs={"pk": self.object.pk})
return reverse("domain-dns-dnssec", kwargs={"domain_pk": self.object.pk})
def post(self, request, *args, **kwargs):
"""Form submission posts to this view."""
@ -903,6 +1043,7 @@ class DomainDNSSECView(DomainFormBaseView):
return self.form_valid(form)
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainDsDataView(DomainFormBaseView):
"""Domain DNSSEC ds data editing view."""
@ -935,7 +1076,7 @@ class DomainDsDataView(DomainFormBaseView):
def get_success_url(self):
"""Redirect to the DS data page for the domain."""
return reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.object.pk})
return reverse("domain-dns-dnssec-dsdata", kwargs={"domain_pk": self.object.pk})
def get_context_data(self, **kwargs):
"""Adjust context from FormMixin for formsets."""
@ -1021,6 +1162,7 @@ class DomainDsDataView(DomainFormBaseView):
return super().form_valid(formset)
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainSecurityEmailView(DomainFormBaseView):
"""Domain security email editing view."""
@ -1041,7 +1183,7 @@ class DomainSecurityEmailView(DomainFormBaseView):
def get_success_url(self):
"""Redirect to the security email page for the domain."""
return reverse("domain-security-email", kwargs={"pk": self.object.pk})
return reverse("domain-security-email", kwargs={"domain_pk": self.object.pk})
def form_valid(self, form):
"""The form is valid, call setter in model."""
@ -1092,6 +1234,7 @@ class DomainSecurityEmailView(DomainFormBaseView):
return redirect(self.get_success_url())
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainUsersView(DomainBaseView):
"""Domain managers page in the domain details."""
@ -1187,6 +1330,7 @@ class DomainUsersView(DomainBaseView):
return context
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainAddUserView(DomainFormBaseView):
"""Inside of a domain's user management, a form for adding users.
@ -1198,7 +1342,7 @@ class DomainAddUserView(DomainFormBaseView):
form_class = DomainAddUserForm
def get_success_url(self):
return reverse("domain-users", kwargs={"pk": self.object.pk})
return reverse("domain-users", kwargs={"domain_pk": self.object.pk})
def form_valid(self, form):
"""Add the specified user to this domain."""
@ -1284,8 +1428,10 @@ class DomainAddUserView(DomainFormBaseView):
messages.success(self.request, f"Added user {email}.")
class DomainInvitationCancelView(SuccessMessageMixin, DomainInvitationPermissionCancelView):
object: DomainInvitation
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainInvitationCancelView(SuccessMessageMixin, UpdateView):
model = DomainInvitation
pk_url_kwarg = "domain_invitation_pk"
fields = []
def post(self, request, *args, **kwargs):
@ -1303,26 +1449,29 @@ class DomainInvitationCancelView(SuccessMessageMixin, DomainInvitationPermission
return HttpResponseRedirect(self.get_success_url())
def get_success_url(self):
return reverse("domain-users", kwargs={"pk": self.object.domain.id})
return reverse("domain-users", kwargs={"domain_pk": self.object.domain.id})
def get_success_message(self, cleaned_data):
return f"Canceled invitation to {self.object.email}."
class DomainDeleteUserView(UserDomainRolePermissionDeleteView):
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainDeleteUserView(DeleteView):
"""Inside of a domain's user management, a form for deleting users."""
object: UserDomainRole # workaround for type mismatch in DeleteView
object: UserDomainRole
model = UserDomainRole
context_object_name = "userdomainrole"
def get_object(self, queryset=None):
"""Custom get_object definition to grab a UserDomainRole object from a domain_id and user_id"""
domain_id = self.kwargs.get("pk")
domain_id = self.kwargs.get("domain_pk")
user_id = self.kwargs.get("user_pk")
return UserDomainRole.objects.get(domain=domain_id, user=user_id)
def get_success_url(self):
"""Refreshes the page after a delete is successful"""
return reverse("domain-users", kwargs={"pk": self.object.domain.id})
return reverse("domain-users", kwargs={"domain_pk": self.object.domain.id})
def get_success_message(self):
"""Returns confirmation content for the deletion event"""

View file

@ -1,32 +1,32 @@
import logging
from collections import defaultdict
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import redirect, render
from django.urls import resolve, reverse
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from django.views.generic import TemplateView
from django.contrib import messages
from django.views.generic import DeleteView, DetailView, TemplateView
from registrar.decorators import (
HAS_PORTFOLIO_DOMAIN_REQUESTS_EDIT,
HAS_PORTFOLIO_DOMAIN_REQUESTS_VIEW_ALL,
IS_DOMAIN_REQUEST_CREATOR,
grant_access,
)
from registrar.forms import domain_request_wizard as forms
from registrar.forms.utility.wizard_form_helper import request_step_list
from registrar.models import DomainRequest
from registrar.models.contact import Contact
from registrar.models.user import User
from registrar.views.utility import StepsHelper
from registrar.views.utility.permission_views import DomainRequestPermissionDeleteView
from registrar.utility.enums import Step, PortfolioDomainRequestStep
from .utility import (
DomainRequestPermissionView,
DomainRequestPermissionWithdrawView,
DomainRequestWizardPermissionView,
DomainRequestPortfolioViewonlyView,
)
logger = logging.getLogger(__name__)
class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
@grant_access(IS_DOMAIN_REQUEST_CREATOR, HAS_PORTFOLIO_DOMAIN_REQUESTS_EDIT)
class DomainRequestWizard(TemplateView):
"""
A common set of methods and configuration.
@ -51,7 +51,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
# NB: this is included here for reference. Do not change it without
# also changing the many places it is hardcoded in the HTML templates
URL_NAMESPACE = "domain-request"
# name for accessing /domain-request/<id>/edit
# name for accessing /domain-request/<domain_request_pk>/edit
EDIT_URL_NAME = "edit-domain-request"
NEW_URL_NAME = "start"
FINISHED_URL_NAME = "finished"
@ -174,7 +174,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
def has_pk(self):
"""Does this wizard know about a DomainRequest database record?"""
return bool(self.kwargs.get("id") is not None)
return bool(self.kwargs.get("domain_request_pk") is not None)
def get_step_enum(self):
"""Determines which step enum we should use for the wizard"""
@ -209,11 +209,11 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
try:
self._domain_request = DomainRequest.objects.get(
creator=creator,
pk=self.kwargs.get("id"),
pk=self.kwargs.get("domain_request_pk"),
)
return self._domain_request
except DomainRequest.DoesNotExist:
logger.debug("DomainRequest id %s did not have a DomainRequest" % id)
logger.debug("DomainRequest id %s did not have a DomainRequest" % self.kwargs.get("domain_request_pk"))
# If a user is creating a request, we assume that perms are handled upstream
if self.request.user.is_org_user(self.request):
@ -292,10 +292,10 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
current_url = resolve(request.path_info).url_name
# if user visited via an "edit" url, associate the id of the
# if user visited via an "edit" url, associate the pk of the
# domain request they are trying to edit to this wizard instance
# and remove any prior wizard data from their session
if current_url == self.EDIT_URL_NAME and "id" in kwargs:
if current_url == self.EDIT_URL_NAME and "domain_request_pk" in kwargs:
del self.storage
# if accessing this class directly, redirect to either to an acknowledgement
@ -474,7 +474,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
def goto(self, step):
self.steps.current = step
return redirect(reverse(f"{self.URL_NAMESPACE}:{step}", kwargs={"id": self.domain_request.id}))
return redirect(reverse(f"{self.URL_NAMESPACE}:{step}", kwargs={"domain_request_pk": self.domain_request.id}))
def goto_next_step(self):
"""Redirects to the next step."""
@ -823,23 +823,12 @@ class Finished(DomainRequestWizard):
return render(self.request, self.template_name, context)
class DomainRequestStatus(DomainRequestPermissionView):
@grant_access(IS_DOMAIN_REQUEST_CREATOR, HAS_PORTFOLIO_DOMAIN_REQUESTS_EDIT)
class DomainRequestStatus(DetailView):
template_name = "domain_request_status.html"
def has_permission(self):
"""
Override of the base has_permission class to account for portfolio permissions
"""
has_base_perms = super().has_permission()
if not has_base_perms:
return False
if self.request.user.is_org_user(self.request):
portfolio = self.request.session.get("portfolio")
if not self.request.user.has_edit_request_portfolio_permission(portfolio):
return False
return True
model = DomainRequest
pk_url_kwarg = "domain_request_pk"
context_object_name = "DomainRequest"
def get_context_data(self, **kwargs):
"""Context override to add a step list to the context"""
@ -854,19 +843,27 @@ class DomainRequestStatus(DomainRequestPermissionView):
return context
class DomainRequestWithdrawConfirmation(DomainRequestPermissionWithdrawView):
@grant_access(IS_DOMAIN_REQUEST_CREATOR, HAS_PORTFOLIO_DOMAIN_REQUESTS_EDIT)
class DomainRequestWithdrawConfirmation(DetailView):
"""This page will ask user to confirm if they want to withdraw
The DomainRequestPermissionView restricts access so that only the
Access is restricted so that only the
`creator` of the domain request may withdraw it.
"""
template_name = "domain_request_withdraw_confirmation.html"
template_name = "domain_request_withdraw_confirmation.html" # DetailView property for what model this is viewing
model = DomainRequest
pk_url_kwarg = "domain_request_pk"
context_object_name = "DomainRequest"
class DomainRequestWithdrawn(DomainRequestPermissionWithdrawView):
@grant_access(IS_DOMAIN_REQUEST_CREATOR, HAS_PORTFOLIO_DOMAIN_REQUESTS_EDIT)
class DomainRequestWithdrawn(DetailView):
# this view renders no template
template_name = ""
model = DomainRequest
pk_url_kwarg = "domain_request_pk"
context_object_name = "DomainRequest"
def get(self, *args, **kwargs):
"""View class that does the actual withdrawing.
@ -874,7 +871,7 @@ class DomainRequestWithdrawn(DomainRequestPermissionWithdrawView):
If user click on withdraw confirm button, this view updates the status
to withdraw and send back to homepage.
"""
domain_request = DomainRequest.objects.get(id=self.kwargs["pk"])
domain_request = DomainRequest.objects.get(id=self.kwargs["domain_request_pk"])
domain_request.withdraw()
domain_request.save()
if self.request.user.is_org_user(self.request):
@ -883,16 +880,16 @@ class DomainRequestWithdrawn(DomainRequestPermissionWithdrawView):
return HttpResponseRedirect(reverse("home"))
class DomainRequestDeleteView(DomainRequestPermissionDeleteView):
@grant_access(IS_DOMAIN_REQUEST_CREATOR, HAS_PORTFOLIO_DOMAIN_REQUESTS_EDIT)
class DomainRequestDeleteView(PermissionRequiredMixin, DeleteView):
"""Delete view for home that allows the end user to delete DomainRequests"""
object: DomainRequest # workaround for type mismatch in DeleteView
model = DomainRequest
pk_url_kwarg = "domain_request_pk"
def has_permission(self):
"""Custom override for has_permission to exclude all statuses, except WITHDRAWN and STARTED"""
has_perm = super().has_permission()
if not has_perm:
return False
status = self.get_object().status
valid_statuses = [DomainRequest.DomainRequestStatus.WITHDRAWN, DomainRequest.DomainRequestStatus.STARTED]
@ -989,8 +986,12 @@ class DomainRequestDeleteView(DomainRequestPermissionDeleteView):
# region Portfolio views
class PortfolioDomainRequestStatusViewOnly(DomainRequestPortfolioViewonlyView):
@grant_access(HAS_PORTFOLIO_DOMAIN_REQUESTS_VIEW_ALL)
class PortfolioDomainRequestStatusViewOnly(DetailView):
template_name = "portfolio_domain_request_status_viewonly.html"
model = DomainRequest
pk_url_kwarg = "domain_request_pk"
context_object_name = "DomainRequest"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)

View file

@ -1,33 +1,33 @@
from django.http import JsonResponse
from django.core.paginator import Paginator
from registrar.decorators import grant_access, ALL
from registrar.models import DomainRequest
from django.utils.dateformat import format
from django.contrib.auth.decorators import login_required
from django.urls import reverse
from django.db.models import Q
@login_required
@grant_access(ALL)
def get_domain_requests_json(request):
"""Given the current request,
get all domain requests that are associated with the request user and exclude the APPROVED ones.
If we are on the portfolio requests page, limit the response to only those requests associated with
the given portfolio."""
domain_request_ids = get_domain_request_ids_from_request(request)
domain_request_ids = _get_domain_request_ids_from_request(request)
objects = DomainRequest.objects.filter(id__in=domain_request_ids)
unfiltered_total = objects.count()
objects = apply_search(objects, request)
objects = apply_status_filter(objects, request)
objects = apply_sorting(objects, request)
objects = _apply_search(objects, request)
objects = _apply_status_filter(objects, request)
objects = _apply_sorting(objects, request)
paginator = Paginator(objects, 10)
page_number = request.GET.get("page", 1)
page_obj = paginator.get_page(page_number)
domain_requests = [
serialize_domain_request(request, domain_request, request.user) for domain_request in page_obj.object_list
_serialize_domain_request(request, domain_request, request.user) for domain_request in page_obj.object_list
]
return JsonResponse(
@ -43,7 +43,7 @@ def get_domain_requests_json(request):
)
def get_domain_request_ids_from_request(request):
def _get_domain_request_ids_from_request(request):
"""Get domain request ids from request.
If portfolio specified, return domain request ids associated with portfolio.
@ -62,7 +62,7 @@ def get_domain_request_ids_from_request(request):
return domain_requests.values_list("id", flat=True)
def apply_search(queryset, request):
def _apply_search(queryset, request):
search_term = request.GET.get("search_term")
is_portfolio = request.GET.get("portfolio")
@ -90,7 +90,7 @@ def apply_search(queryset, request):
return queryset
def apply_status_filter(queryset, request):
def _apply_status_filter(queryset, request):
status_param = request.GET.get("status")
if status_param:
status_list = status_param.split(",")
@ -105,7 +105,7 @@ def apply_status_filter(queryset, request):
return queryset
def apply_sorting(queryset, request):
def _apply_sorting(queryset, request):
sort_by = request.GET.get("sort_by", "id") # Default to 'id'
order = request.GET.get("order", "asc") # Default to 'asc'
@ -118,7 +118,7 @@ def apply_sorting(queryset, request):
return queryset.order_by(sort_by)
def serialize_domain_request(request, domain_request, user):
def _serialize_domain_request(request, domain_request, user):
deletable_statuses = [
DomainRequest.DomainRequestStatus.STARTED,
@ -154,9 +154,9 @@ def serialize_domain_request(request, domain_request, user):
# Map the action label to corresponding URLs and icons
action_url_map = {
"Edit": reverse("edit-domain-request", kwargs={"id": domain_request.id}),
"Manage": reverse("domain-request-status", kwargs={"pk": domain_request.id}),
"View": reverse("domain-request-status-viewonly", kwargs={"pk": domain_request.id}),
"Edit": reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.id}),
"Manage": reverse("domain-request-status", kwargs={"domain_request_pk": domain_request.id}),
"View": reverse("domain-request-status-viewonly", kwargs={"domain_request_pk": domain_request.id}),
}
svg_icon_map = {"Edit": "edit", "Manage": "settings", "View": "visibility"}

View file

@ -1,15 +1,15 @@
import logging
from django.http import JsonResponse
from django.core.paginator import Paginator
from registrar.decorators import grant_access, ALL
from registrar.models import UserDomainRole, Domain, DomainInformation, User
from django.contrib.auth.decorators import login_required
from django.urls import reverse
from django.db.models import Q
logger = logging.getLogger(__name__)
@login_required
@grant_access(ALL)
def get_domains_json(request):
"""Given the current request,
get all domains that are associated with the UserDomainRole object"""
@ -142,7 +142,7 @@ def serialize_domain(domain, request):
"state": domain.state,
"state_display": domain.state_display(request),
"get_state_help_text": domain.get_state_help_text(),
"action_url": reverse("domain", kwargs={"pk": domain.id}),
"action_url": reverse("domain", kwargs={"domain_pk": domain.id}),
"action_label": ("View" if view_only else "Manage"),
"svg_icon": ("visibility" if view_only else "settings"),
"domain_info__sub_organization": suborganization_name,

View file

@ -1,6 +1,9 @@
from django.shortcuts import render
from registrar.decorators import grant_access, ALL
@grant_access(ALL)
def index(request):
"""This page is available to anyone without logging in."""
context = {}

View file

@ -4,37 +4,38 @@ from django.http import JsonResponse
from django.core.paginator import Paginator
from django.shortcuts import get_object_or_404
from django.views import View
from registrar.decorators import HAS_PORTFOLIO_MEMBERS_ANY_PERM, grant_access
from registrar.models import UserDomainRole, Domain, DomainInformation, User
from django.urls import reverse
from django.db.models import Q
from registrar.models.domain_invitation import DomainInvitation
from registrar.views.utility.mixins import PortfolioMemberDomainsPermission
logger = logging.getLogger(__name__)
class PortfolioMemberDomainsJson(PortfolioMemberDomainsPermission, View):
@grant_access(HAS_PORTFOLIO_MEMBERS_ANY_PERM)
class PortfolioMemberDomainsJson(View):
def get(self, request):
"""Given the current request,
get all domains that are associated with the portfolio, or
associated with the member/invited member"""
domain_ids = self.get_domain_ids_from_request(request)
domain_ids = self._get_domain_ids_from_request(request)
objects = Domain.objects.filter(id__in=domain_ids).select_related("domain_info__sub_organization")
unfiltered_total = objects.count()
objects = self.apply_search(objects, request)
objects = self.apply_sorting(objects, request)
objects = self._apply_search(objects, request)
objects = self._apply_sorting(objects, request)
paginator = Paginator(objects, self.get_page_size(request))
paginator = Paginator(objects, self._get_page_size(request))
page_number = request.GET.get("page")
page_obj = paginator.get_page(page_number)
member_id = request.GET.get("member_id")
domains = [self.serialize_domain(domain, member_id, request.user) for domain in page_obj.object_list]
domains = [self._serialize_domain(domain, member_id, request.user) for domain in page_obj.object_list]
return JsonResponse(
{
@ -48,7 +49,7 @@ class PortfolioMemberDomainsJson(PortfolioMemberDomainsPermission, View):
}
)
def get_page_size(self, request):
def _get_page_size(self, request):
"""Gets the page size.
If member_only, need to return the entire result set every time, so need
@ -65,7 +66,7 @@ class PortfolioMemberDomainsJson(PortfolioMemberDomainsPermission, View):
# later
return 1000
def get_domain_ids_from_request(self, request):
def _get_domain_ids_from_request(self, request):
"""Get domain ids from request.
request.get.email - email address of invited member
@ -100,13 +101,13 @@ class PortfolioMemberDomainsJson(PortfolioMemberDomainsPermission, View):
logger.warning("Invalid search criteria, returning empty results list")
return []
def apply_search(self, queryset, request):
def _apply_search(self, queryset, request):
search_term = request.GET.get("search_term")
if search_term:
queryset = queryset.filter(Q(name__icontains=search_term))
return queryset
def apply_sorting(self, queryset, request):
def _apply_sorting(self, queryset, request):
# Get the sorting parameters from the request
sort_by = request.GET.get("sort_by", "name")
order = request.GET.get("order", "asc")
@ -141,7 +142,7 @@ class PortfolioMemberDomainsJson(PortfolioMemberDomainsPermission, View):
return queryset
def serialize_domain(self, domain, member_id, user):
def _serialize_domain(self, domain, member_id, user):
suborganization_name = None
try:
domain_info = domain.domain_info
@ -176,7 +177,7 @@ class PortfolioMemberDomainsJson(PortfolioMemberDomainsPermission, View):
"state": domain.state,
"state_display": domain.state_display(),
"get_state_help_text": domain.get_state_help_text(),
"action_url": reverse("domain", kwargs={"pk": domain.id}),
"action_url": reverse("domain", kwargs={"domain_pk": domain.id}),
"action_label": ("View" if view_only else "Manage"),
"svg_icon": ("visibility" if view_only else "settings"),
"domain_info__sub_organization": suborganization_name,

View file

@ -6,16 +6,17 @@ from django.contrib.postgres.aggregates import ArrayAgg
from django.urls import reverse
from django.views import View
from registrar.decorators import HAS_PORTFOLIO_MEMBERS_ANY_PERM, grant_access
from registrar.models.domain_invitation import DomainInvitation
from registrar.models.portfolio_invitation import PortfolioInvitation
from registrar.models.user_portfolio_permission import UserPortfolioPermission
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
from registrar.views.utility.mixins import PortfolioMembersPermission
from registrar.models.utility.orm_helper import ArrayRemoveNull
from django.contrib.postgres.aggregates import StringAgg
class PortfolioMembersJson(PortfolioMembersPermission, View):
@grant_access(HAS_PORTFOLIO_MEMBERS_ANY_PERM)
class PortfolioMembersJson(View):
def get(self, request):
"""Fetch members (permissions and invitations) for the given portfolio."""
@ -236,7 +237,7 @@ class PortfolioMembersJson(PortfolioMembersPermission, View):
),
# split domain_info array values into ids to form urls, and names
"domain_urls": [
reverse("domain", kwargs={"pk": domain_info.split(":")[0]}) for domain_info in domain_info_list
reverse("domain", kwargs={"domain_pk": domain_info.split(":")[0]}) for domain_info in domain_info_list
],
"domain_names": [domain_info.split(":")[1] for domain_info in domain_info_list],
"is_admin": is_admin,

View file

@ -5,14 +5,26 @@ from django.http import Http404, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.views.generic import DetailView
from django.contrib import messages
from registrar.decorators import (
HAS_PORTFOLIO_DOMAIN_REQUESTS_ANY_PERM,
HAS_PORTFOLIO_DOMAINS_ANY_PERM,
HAS_PORTFOLIO_MEMBERS_ANY_PERM,
HAS_PORTFOLIO_MEMBERS_EDIT,
IS_PORTFOLIO_MEMBER,
grant_access,
)
from registrar.forms import portfolio as portfolioForms
from registrar.models import Portfolio, User
from registrar.models.domain import Domain
from registrar.models.domain_invitation import DomainInvitation
from registrar.models.portfolio_invitation import PortfolioInvitation
from registrar.models.user_domain_role import UserDomainRole
from registrar.models.user_portfolio_permission import UserPortfolioPermission
from registrar.models import (
Domain,
DomainInvitation,
Portfolio,
PortfolioInvitation,
User,
UserDomainRole,
UserPortfolioPermission,
)
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
from registrar.utility.email import EmailSendingError
from registrar.utility.email_invitations import (
@ -24,18 +36,6 @@ from registrar.utility.email_invitations import (
)
from registrar.utility.errors import MissingEmailError
from registrar.utility.enums import DefaultUserValues
from registrar.views.utility.mixins import PortfolioMemberPermission
from registrar.views.utility.permission_views import (
PortfolioDomainRequestsPermissionView,
PortfolioDomainsPermissionView,
PortfolioBasePermissionView,
NoPortfolioDomainsPermissionView,
PortfolioMemberDomainsPermissionView,
PortfolioMemberDomainsEditPermissionView,
PortfolioMemberEditPermissionView,
PortfolioMemberPermissionView,
PortfolioMembersPermissionView,
)
from django.views.generic import View
from django.views.generic.edit import FormMixin
from django.db import IntegrityError
@ -46,7 +46,8 @@ from registrar.views.utility.invitation_helper import get_org_membership
logger = logging.getLogger(__name__)
class PortfolioDomainsView(PortfolioDomainsPermissionView, View):
@grant_access(HAS_PORTFOLIO_DOMAINS_ANY_PERM)
class PortfolioDomainsView(View):
template_name = "portfolio_domains.html"
@ -59,7 +60,8 @@ class PortfolioDomainsView(PortfolioDomainsPermissionView, View):
return render(request, "portfolio_domains.html", context)
class PortfolioDomainRequestsView(PortfolioDomainRequestsPermissionView, View):
@grant_access(HAS_PORTFOLIO_DOMAIN_REQUESTS_ANY_PERM)
class PortfolioDomainRequestsView(View):
template_name = "portfolio_requests.html"
@ -67,8 +69,10 @@ class PortfolioDomainRequestsView(PortfolioDomainRequestsPermissionView, View):
return render(request, "portfolio_requests.html")
class PortfolioMemberView(PortfolioMemberPermissionView, View):
@grant_access(HAS_PORTFOLIO_MEMBERS_ANY_PERM)
class PortfolioMemberView(DetailView, View):
model = Portfolio
context_object_name = "portfolio"
template_name = "portfolio_member.html"
def get(self, request, pk):
@ -109,7 +113,8 @@ class PortfolioMemberView(PortfolioMemberPermissionView, View):
)
class PortfolioMemberDeleteView(PortfolioMemberPermission, View):
@grant_access(HAS_PORTFOLIO_MEMBERS_ANY_PERM)
class PortfolioMemberDeleteView(View):
def post(self, request, pk):
"""
@ -186,8 +191,10 @@ class PortfolioMemberDeleteView(PortfolioMemberPermission, View):
messages.warning(self.request, "Could not send email notification to existing organization admins.")
class PortfolioMemberEditView(PortfolioMemberEditPermissionView, View):
@grant_access(HAS_PORTFOLIO_MEMBERS_EDIT)
class PortfolioMemberEditView(DetailView, View):
model = Portfolio
context_object_name = "portfolio"
template_name = "portfolio_member_permissions.html"
form_class = portfolioForms.PortfolioMemberForm
@ -266,7 +273,8 @@ class PortfolioMemberEditView(PortfolioMemberEditPermissionView, View):
messages.warning(self.request, "Could not send email notification to existing organization admins.")
class PortfolioMemberDomainsView(PortfolioMemberDomainsPermissionView, View):
@grant_access(HAS_PORTFOLIO_MEMBERS_ANY_PERM)
class PortfolioMemberDomainsView(View):
template_name = "portfolio_member_domains.html"
@ -284,8 +292,10 @@ class PortfolioMemberDomainsView(PortfolioMemberDomainsPermissionView, View):
)
class PortfolioMemberDomainsEditView(PortfolioMemberDomainsEditPermissionView, View):
@grant_access(HAS_PORTFOLIO_MEMBERS_EDIT)
class PortfolioMemberDomainsEditView(DetailView, View):
model = Portfolio
context_object_name = "portfolio"
template_name = "portfolio_member_domains_edit.html"
def get(self, request, pk):
@ -394,8 +404,10 @@ class PortfolioMemberDomainsEditView(PortfolioMemberDomainsEditPermissionView, V
UserDomainRole.objects.filter(domain_id__in=removed_domain_ids, user=member).delete()
class PortfolioInvitedMemberView(PortfolioMemberPermissionView, View):
@grant_access(HAS_PORTFOLIO_MEMBERS_ANY_PERM)
class PortfolioInvitedMemberView(DetailView, View):
model = Portfolio
context_object_name = "portfolio"
template_name = "portfolio_member.html"
# form_class = PortfolioInvitedMemberForm
@ -436,7 +448,8 @@ class PortfolioInvitedMemberView(PortfolioMemberPermissionView, View):
)
class PortfolioInvitedMemberDeleteView(PortfolioMemberPermission, View):
@grant_access(HAS_PORTFOLIO_MEMBERS_ANY_PERM)
class PortfolioInvitedMemberDeleteView(View):
def post(self, request, pk):
"""
@ -479,8 +492,10 @@ class PortfolioInvitedMemberDeleteView(PortfolioMemberPermission, View):
messages.warning(self.request, "Could not send email notification to existing organization admins.")
class PortfolioInvitedMemberEditView(PortfolioMemberEditPermissionView, View):
@grant_access(HAS_PORTFOLIO_MEMBERS_EDIT)
class PortfolioInvitedMemberEditView(DetailView, View):
model = Portfolio
context_object_name = "portfolio"
template_name = "portfolio_member_permissions.html"
form_class = portfolioForms.PortfolioInvitedMemberForm
@ -548,7 +563,8 @@ class PortfolioInvitedMemberEditView(PortfolioMemberEditPermissionView, View):
messages.warning(self.request, "Could not send email notification to existing organization admins.")
class PortfolioInvitedMemberDomainsView(PortfolioMemberDomainsPermissionView, View):
@grant_access(HAS_PORTFOLIO_MEMBERS_ANY_PERM)
class PortfolioInvitedMemberDomainsView(View):
template_name = "portfolio_member_domains.html"
@ -564,8 +580,11 @@ class PortfolioInvitedMemberDomainsView(PortfolioMemberDomainsPermissionView, Vi
)
class PortfolioInvitedMemberDomainsEditView(PortfolioMemberDomainsEditPermissionView, View):
@grant_access(HAS_PORTFOLIO_MEMBERS_EDIT)
class PortfolioInvitedMemberDomainsEditView(DetailView, View):
model = Portfolio
context_object_name = "portfolio"
template_name = "portfolio_member_domains_edit.html"
def get(self, request, pk):
@ -690,7 +709,8 @@ class PortfolioInvitedMemberDomainsEditView(PortfolioMemberDomainsEditPermission
).update(status=DomainInvitation.DomainInvitationStatus.CANCELED)
class PortfolioNoDomainsView(NoPortfolioDomainsPermissionView, View):
@grant_access(IS_PORTFOLIO_MEMBER)
class PortfolioNoDomainsView(View):
"""Some users have access to the underlying portfolio, but not any domains.
This is a custom view which explains that to the user - and denotes who to contact.
"""
@ -719,7 +739,8 @@ class PortfolioNoDomainsView(NoPortfolioDomainsPermissionView, View):
return context
class PortfolioNoDomainRequestsView(NoPortfolioDomainsPermissionView, View):
@grant_access(IS_PORTFOLIO_MEMBER)
class PortfolioNoDomainRequestsView(View):
"""Some users have access to the underlying portfolio, but not any domain requests.
This is a custom view which explains that to the user - and denotes who to contact.
"""
@ -748,7 +769,8 @@ class PortfolioNoDomainRequestsView(NoPortfolioDomainsPermissionView, View):
return context
class PortfolioOrganizationView(PortfolioBasePermissionView, FormMixin):
@grant_access(IS_PORTFOLIO_MEMBER)
class PortfolioOrganizationView(DetailView, FormMixin):
"""
View to handle displaying and updating the portfolio's organization details.
"""
@ -810,7 +832,8 @@ class PortfolioOrganizationView(PortfolioBasePermissionView, FormMixin):
return reverse("organization")
class PortfolioSeniorOfficialView(PortfolioBasePermissionView, FormMixin):
@grant_access(IS_PORTFOLIO_MEMBER)
class PortfolioSeniorOfficialView(DetailView, FormMixin):
"""
View to handle displaying and updating the portfolio's senior official details.
For now, this view is readonly.
@ -841,7 +864,8 @@ class PortfolioSeniorOfficialView(PortfolioBasePermissionView, FormMixin):
return self.render_to_response(self.get_context_data(form=form))
class PortfolioMembersView(PortfolioMembersPermissionView, View):
@grant_access(HAS_PORTFOLIO_MEMBERS_ANY_PERM)
class PortfolioMembersView(View):
template_name = "portfolio_members.html"
@ -850,10 +874,13 @@ class PortfolioMembersView(PortfolioMembersPermissionView, View):
return render(request, "portfolio_members.html")
class PortfolioAddMemberView(PortfolioMembersPermissionView, FormMixin):
@grant_access(HAS_PORTFOLIO_MEMBERS_ANY_PERM)
class PortfolioAddMemberView(DetailView, FormMixin):
template_name = "portfolio_members_add_new.html"
form_class = portfolioForms.PortfolioNewMemberForm
model = Portfolio
context_object_name = "portfolio"
def get(self, request, *args, **kwargs):
"""Handle GET requests to display the form."""

View file

@ -6,19 +6,17 @@ from django.shortcuts import render
from django.contrib import admin
from django.db.models import Avg, F
from registrar.views.utility.mixins import DomainAndRequestsReportsPermission, PortfolioReportsPermission
from registrar.decorators import ALL, HAS_PORTFOLIO_MEMBERS_VIEW, IS_STAFF, grant_access
from .. import models
import datetime
from django.utils import timezone
from django.contrib.admin.views.decorators import staff_member_required
from django.utils.decorators import method_decorator
from registrar.utility import csv_export
import logging
logger = logging.getLogger(__name__)
@method_decorator(staff_member_required, name="dispatch")
@grant_access(IS_STAFF)
class AnalyticsView(View):
def get(self, request):
thirty_days_ago = datetime.datetime.today() - datetime.timedelta(days=30)
@ -178,7 +176,7 @@ class AnalyticsView(View):
return render(request, "admin/analytics.html", context)
@method_decorator(staff_member_required, name="dispatch")
@grant_access(IS_STAFF)
class ExportDataType(View):
def get(self, request, *args, **kwargs):
# match the CSV example with all the fields
@ -188,7 +186,8 @@ class ExportDataType(View):
return response
class ExportDataTypeUser(DomainAndRequestsReportsPermission, View):
@grant_access(ALL)
class ExportDataTypeUser(View):
"""Returns a domain report for a given user on the request"""
def get(self, request, *args, **kwargs):
@ -199,7 +198,8 @@ class ExportDataTypeUser(DomainAndRequestsReportsPermission, View):
return response
class ExportMembersPortfolio(PortfolioReportsPermission, View):
@grant_access(HAS_PORTFOLIO_MEMBERS_VIEW)
class ExportMembersPortfolio(View):
"""Returns a members report for a given portfolio"""
def get(self, request, *args, **kwargs):
@ -227,7 +227,7 @@ class ExportMembersPortfolio(PortfolioReportsPermission, View):
return response
@method_decorator(staff_member_required, name="dispatch")
@grant_access(IS_STAFF)
class ExportDataFull(View):
def get(self, request, *args, **kwargs):
# Smaller export based on 1
@ -237,7 +237,7 @@ class ExportDataFull(View):
return response
@method_decorator(staff_member_required, name="dispatch")
@grant_access(IS_STAFF)
class ExportDataFederal(View):
def get(self, request, *args, **kwargs):
# Federal only
@ -247,7 +247,7 @@ class ExportDataFederal(View):
return response
@method_decorator(staff_member_required, name="dispatch")
@grant_access(IS_STAFF)
class ExportDomainRequestDataFull(View):
"""Generates a downloaded report containing all Domain Requests (except started)"""
@ -259,7 +259,7 @@ class ExportDomainRequestDataFull(View):
return response
@method_decorator(staff_member_required, name="dispatch")
@grant_access(IS_STAFF)
class ExportDataDomainsGrowth(View):
def get(self, request, *args, **kwargs):
start_date = request.GET.get("start_date", "")
@ -272,7 +272,7 @@ class ExportDataDomainsGrowth(View):
return response
@method_decorator(staff_member_required, name="dispatch")
@grant_access(IS_STAFF)
class ExportDataRequestsGrowth(View):
def get(self, request, *args, **kwargs):
start_date = request.GET.get("start_date", "")
@ -285,7 +285,7 @@ class ExportDataRequestsGrowth(View):
return response
@method_decorator(staff_member_required, name="dispatch")
@grant_access(IS_STAFF)
class ExportDataManagedDomains(View):
def get(self, request, *args, **kwargs):
start_date = request.GET.get("start_date", "")
@ -297,7 +297,7 @@ class ExportDataManagedDomains(View):
return response
@method_decorator(staff_member_required, name="dispatch")
@grant_access(IS_STAFF)
class ExportDataUnmanagedDomains(View):
def get(self, request, *args, **kwargs):
start_date = request.GET.get("start_date", "")

View file

@ -4,6 +4,7 @@ from django.db.models import ForeignKey, OneToOneField, ManyToManyField, ManyToO
from django.shortcuts import render, get_object_or_404, redirect
from django.views import View
from registrar.decorators import IS_STAFF, grant_access
from registrar.models.domain import Domain
from registrar.models.domain_request import DomainRequest
from registrar.models.user import User
@ -18,6 +19,7 @@ from registrar.utility.db_helpers import ignore_unique_violation
logger = logging.getLogger(__name__)
@grant_access(IS_STAFF)
class TransferUserView(View):
"""Transfer user methods that set up the transfer_user template and handle the forms on it."""

View file

@ -5,22 +5,25 @@ import logging
from django.contrib import messages
from django.http import QueryDict
from django.views.generic import DetailView
from django.views.generic.edit import FormMixin
from registrar.decorators import ALL, grant_access
from registrar.forms.user_profile import UserProfileForm, FinishSetupProfileForm
from django.urls import NoReverseMatch, reverse
from registrar.models.user import User
from registrar.models.utility.generic_helper import replace_url_queryparams
from registrar.views.utility.permission_views import UserProfilePermissionView
logger = logging.getLogger(__name__)
class UserProfileView(UserProfilePermissionView, FormMixin):
@grant_access(ALL)
class UserProfileView(DetailView, FormMixin):
"""
Base View for the User Profile. Handles getting and setting the User Profile
"""
model = User
context_object_name = "user"
template_name = "profile.html"
form_class = UserProfileForm
base_view_name = "user-profile"

View file

@ -1,13 +1,4 @@
from .steps_helper import StepsHelper
from .always_404 import always_404
from .permission_views import (
DomainPermissionView,
DomainRequestPermissionView,
DomainRequestPermissionWithdrawView,
DomainRequestWizardPermissionView,
PortfolioMembersPermission,
DomainRequestPortfolioViewonlyView,
DomainInvitationPermissionCancelView,
)
from .api_views import get_senior_official_from_federal_agency_json

View file

@ -1,9 +1,8 @@
import logging
from django.http import JsonResponse
from django.forms.models import model_to_dict
from registrar.decorators import IS_STAFF, grant_access
from registrar.models import FederalAgency, SeniorOfficial, DomainRequest
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from registrar.utility.admin_helpers import get_action_needed_reason_default_email, get_rejection_reason_default_email
from registrar.models.portfolio import Portfolio
from registrar.utility.constants import BranchChoices
@ -11,8 +10,7 @@ from registrar.utility.constants import BranchChoices
logger = logging.getLogger(__name__)
@login_required
@staff_member_required
@grant_access(IS_STAFF)
def get_senior_official_from_federal_agency_json(request):
"""Returns federal_agency information as a JSON"""
@ -39,8 +37,7 @@ def get_senior_official_from_federal_agency_json(request):
return JsonResponse({"error": "Senior Official not found"}, status=404)
@login_required
@staff_member_required
@grant_access(IS_STAFF)
def get_portfolio_json(request):
"""Returns portfolio information as a JSON"""
@ -96,8 +93,7 @@ def get_portfolio_json(request):
return JsonResponse(portfolio_dict)
@login_required
@staff_member_required
@grant_access(IS_STAFF)
def get_suborganization_list_json(request):
"""Returns suborganization list information for a portfolio as a JSON"""
@ -119,8 +115,7 @@ def get_suborganization_list_json(request):
return JsonResponse({"results": results, "pagination": {"more": False}})
@login_required
@staff_member_required
@grant_access(IS_STAFF)
def get_federal_and_portfolio_types_from_federal_agency_json(request):
"""Returns specific portfolio information as a JSON. Request must have
both agency_name and organization_type."""
@ -148,8 +143,7 @@ def get_federal_and_portfolio_types_from_federal_agency_json(request):
return JsonResponse(response_data)
@login_required
@staff_member_required
@grant_access(IS_STAFF)
def get_action_needed_email_for_user_json(request):
"""Returns a default action needed email for a given user"""
@ -173,8 +167,7 @@ def get_action_needed_email_for_user_json(request):
return JsonResponse({"email": email}, status=200)
@login_required
@staff_member_required
@grant_access(IS_STAFF)
def get_rejection_email_for_user_json(request):
"""Returns a default rejection email for a given user"""

View file

@ -35,3 +35,10 @@ def custom_403_error_view(request, exception=None, context=None):
if context is None:
context = {}
return render(request, "403.html", context=context, status=403)
def custom_404_error_view(request, exception=None, context=None):
"""Used to redirect 404 errors to a custom view"""
if context is None:
context = {}
return render(request, "404.html", context=context, status=404)

View file

@ -1,14 +1,5 @@
"""Permissions-related mixin classes."""
"""Mixin classes."""
from django.contrib.auth.mixins import PermissionRequiredMixin
from registrar.models import (
Domain,
DomainRequest,
DomainInvitation,
DomainInformation,
UserDomainRole,
)
import logging
@ -143,486 +134,3 @@ class OrderableFieldsMixin:
# Infer the column name in a similar manner to how Django does
method.short_description = field.replace("_", " ")
return method
class PermissionsLoginMixin(PermissionRequiredMixin):
"""Mixin that redirects to login page if not logged in, otherwise 403."""
def handle_no_permission(self):
self.raise_exception = self.request.user.is_authenticated
return super().handle_no_permission()
class DomainAndRequestsReportsPermission(PermissionsLoginMixin):
"""Permission mixin for domain and requests csv downloads"""
def has_permission(self):
"""Check if this user has access to this domain.
The user is in self.request.user and the domain needs to be looked
up from the domain's primary key in self.kwargs["pk"]
"""
if not self.request.user.is_authenticated:
return False
if self.request.user.is_restricted():
return False
return True
class PortfolioReportsPermission(PermissionsLoginMixin):
"""Permission mixin for portfolio csv downloads"""
def has_permission(self):
"""Check if this user has access to this domain.
The user is in self.request.user and the domain needs to be looked
up from the domain's primary key in self.kwargs["pk"]
"""
if not self.request.user.is_authenticated:
return False
if self.request.user.is_restricted():
return False
portfolio = self.request.session.get("portfolio")
if not self.request.user.has_view_members_portfolio_permission(portfolio):
return False
return self.request.user.is_org_user(self.request)
class DomainPermission(PermissionsLoginMixin):
"""Permission mixin that redirects to domain if user has access,
otherwise 403"""
def has_permission(self):
"""Check if this user has access to this domain.
The user is in self.request.user and the domain needs to be looked
up from the domain's primary key in self.kwargs["pk"]
"""
if not self.request.user.is_authenticated:
return False
if self.request.user.is_restricted():
return False
pk = self.kwargs["pk"]
# If pk is none then something went very wrong...
if pk is None:
raise ValueError("Primary key is None")
# test if domain in editable state
if not self.in_editable_state(pk):
return False
if self.can_access_other_user_domains(pk):
return True
# user needs to have a role on the domain
if not UserDomainRole.objects.filter(user=self.request.user, domain__id=pk).exists():
return self.can_access_domain_via_portfolio(pk)
# if we need to check more about the nature of role, do it here.
return True
def can_access_domain_via_portfolio(self, pk):
"""Most views should not allow permission to portfolio users.
If particular views allow access to the domain pages, they will need to override
this function.
"""
return False
def in_editable_state(self, pk):
"""Is the domain in an editable state"""
requested_domain = None
if Domain.objects.filter(id=pk).exists():
requested_domain = Domain.objects.get(id=pk)
# if domain is editable return true
if requested_domain and requested_domain.is_editable():
return True
return False
def can_access_other_user_domains(self, pk):
"""Checks to see if an authorized user (staff or superuser)
can access a domain that they did not create or was invited to.
"""
# Check if the user is permissioned...
user_is_analyst_or_superuser = self.request.user.has_perm(
"registrar.analyst_access_permission"
) or self.request.user.has_perm("registrar.full_access_permission")
if not user_is_analyst_or_superuser:
return False
# Check if the user is attempting a valid edit action.
# In other words, if the analyst/admin did not click
# the 'Manage Domain' button in /admin,
# then they cannot access this page.
session = self.request.session
can_do_action = (
"analyst_action" in session
and "analyst_action_location" in session
and session["analyst_action_location"] == pk
)
if not can_do_action:
return False
# Analysts may manage domains, when they are in these statuses:
valid_domain_statuses = [
DomainRequest.DomainRequestStatus.APPROVED,
DomainRequest.DomainRequestStatus.IN_REVIEW,
DomainRequest.DomainRequestStatus.REJECTED,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
# Edge case - some domains do not have
# a status or DomainInformation... aka a status of 'None'.
# It is necessary to access those to correct errors.
None,
]
requested_domain = None
if DomainInformation.objects.filter(id=pk).exists():
requested_domain = DomainInformation.objects.get(id=pk)
# if no domain information or domain request exist, the user
# should be able to manage the domain; however, if domain information
# and domain request exist, and domain request is not in valid status,
# user should not be able to manage domain
if (
requested_domain
and requested_domain.domain_request
and requested_domain.domain_request.status not in valid_domain_statuses
):
return False
# Valid session keys exist,
# the user is permissioned,
# and it is in a valid status
return True
class DomainRequestPermission(PermissionsLoginMixin):
"""Permission mixin that redirects to domain request if user
has access, otherwise 403"""
def has_permission(self):
"""Check if this user has access to this domain request.
The user is in self.request.user and the domain needs to be looked
up from the domain's primary key in self.kwargs["pk"]
"""
if not self.request.user.is_authenticated:
return False
# user needs to be the creator of the domain request
# this query is empty if there isn't a domain request with this
# id and this user as creator
if not DomainRequest.objects.filter(creator=self.request.user, id=self.kwargs["pk"]).exists():
return False
return True
class DomainRequestPortfolioViewonlyPermission(PermissionsLoginMixin):
"""Permission mixin that redirects to domain request if user
has access, otherwise 403"""
def has_permission(self):
"""Check if this user has access to this domain request.
The user is in self.request.user and the domain needs to be looked
up from the domain's primary key in self.kwargs["pk"]
"""
if not self.request.user.is_authenticated:
return False
if not self.request.user.is_org_user(self.request):
return False
portfolio = self.request.session.get("portfolio")
if not self.request.user.has_view_all_requests_portfolio_permission(portfolio):
return False
return True
class UserDeleteDomainRolePermission(PermissionsLoginMixin):
"""Permission mixin for UserDomainRole if user
has access, otherwise 403"""
def has_permission(self):
"""Check if this user has access to this domain request.
The user is in self.request.user and the domain needs to be looked
up from the domain's primary key in self.kwargs["pk"]
"""
domain_pk = self.kwargs["pk"]
user_pk = self.kwargs["user_pk"]
if not self.request.user.is_authenticated:
return False
# Check if the UserDomainRole object exists, then check
# if the user requesting the delete has permissions to do so
has_delete_permission = UserDomainRole.objects.filter(
user=user_pk,
domain=domain_pk,
domain__permissions__user=self.request.user,
).exists()
user_is_analyst_or_superuser = self.request.user.has_perm(
"registrar.analyst_access_permission"
) or self.request.user.has_perm("registrar.full_access_permission")
if not (has_delete_permission or user_is_analyst_or_superuser):
return False
return True
class DomainRequestPermissionWithdraw(PermissionsLoginMixin):
"""Permission mixin that redirects to withdraw action on domain request
if user has access, otherwise 403"""
def has_permission(self):
"""Check if this user has access to withdraw this domain request."""
if not self.request.user.is_authenticated:
return False
# user needs to be the creator of the domain request
# this query is empty if there isn't a domain request with this
# id and this user as creator
if not DomainRequest.objects.filter(creator=self.request.user, id=self.kwargs["pk"]).exists():
return False
# Restricted users should not be able to withdraw domain requests
if self.request.user.is_restricted():
return False
return True
class DomainRequestWizardPermission(PermissionsLoginMixin):
"""Permission mixin that redirects to start or edit domain request if
user has access, otherwise 403"""
def has_permission(self):
"""Check if this user has permission to start or edit a domain request.
The user is in self.request.user
"""
if not self.request.user.is_authenticated:
return False
# The user has an ineligible flag
if self.request.user.is_restricted():
return False
# If the user is an org user and doesn't have add/edit perms, forbid this
if self.request.user.is_org_user(self.request):
portfolio = self.request.session.get("portfolio")
if not self.request.user.has_edit_request_portfolio_permission(portfolio):
return False
# user needs to be the creator of the domain request to edit it.
id = self.kwargs.get("id") if hasattr(self, "kwargs") else None
if not id:
domain_request_wizard = self.request.session.get("wizard_domain_request")
if domain_request_wizard:
id = domain_request_wizard.get("domain_request_id")
# If no id is provided, we can assume that the user is starting a new request.
# If one IS provided, check that they are the original creator of it.
if id:
if not DomainRequest.objects.filter(creator=self.request.user, id=id).exists():
return False
return True
class DomainInvitationPermission(PermissionsLoginMixin):
"""Permission mixin that redirects to domain invitation if user has
access, otherwise 403"
A user has access to a domain invitation if they have a role on the
associated domain.
"""
def has_permission(self):
"""Check if this user has a role on the domain of this invitation."""
if not self.request.user.is_authenticated:
return False
if not DomainInvitation.objects.filter(
id=self.kwargs["pk"], domain__permissions__user=self.request.user
).exists():
return False
return True
class UserProfilePermission(PermissionsLoginMixin):
"""Permission mixin that redirects to user profile if user
has access, otherwise 403"""
def has_permission(self):
"""Check if this user has access.
If the user is authenticated, they have access
"""
# Check if the user is authenticated
if not self.request.user.is_authenticated:
return False
return True
class PortfolioBasePermission(PermissionsLoginMixin):
"""Permission mixin that redirects to portfolio pages if user
has access, otherwise 403"""
def has_permission(self):
"""Check if this user has access to this portfolio.
The user is in self.request.user and the portfolio can be looked
up from the portfolio's primary key in self.kwargs["pk"]
"""
if not self.request.user.is_authenticated:
return False
return self.request.user.is_org_user(self.request)
class PortfolioDomainsPermission(PortfolioBasePermission):
"""Permission mixin that allows access to portfolio domain pages if user
has access, otherwise 403"""
def has_permission(self):
"""Check if this user has access to domains for this portfolio.
The user is in self.request.user and the portfolio can be looked
up from the portfolio's primary key in self.kwargs["pk"]"""
portfolio = self.request.session.get("portfolio")
if not self.request.user.has_any_domains_portfolio_permission(portfolio):
return False
return super().has_permission()
class PortfolioDomainRequestsPermission(PortfolioBasePermission):
"""Permission mixin that allows access to portfolio domain request pages if user
has access, otherwise 403"""
def has_permission(self):
"""Check if this user has access to domain requests for this portfolio.
The user is in self.request.user and the portfolio can be looked
up from the portfolio's primary key in self.kwargs["pk"]"""
portfolio = self.request.session.get("portfolio")
if not self.request.user.has_any_requests_portfolio_permission(portfolio):
return False
return super().has_permission()
class PortfolioMembersPermission(PortfolioBasePermission):
"""Permission mixin that allows access to portfolio members pages if user
has access, otherwise 403"""
def has_permission(self):
"""Check if this user has access to members for this portfolio.
The user is in self.request.user and the portfolio can be looked
up from the portfolio's primary key in self.kwargs["pk"]"""
portfolio = self.request.session.get("portfolio")
if not self.request.user.has_view_members_portfolio_permission(
portfolio
) and not self.request.user.has_edit_members_portfolio_permission(portfolio):
return False
return super().has_permission()
class PortfolioMemberPermission(PortfolioBasePermission):
"""Permission mixin that allows access to portfolio member or invited member pages if user
has access, otherwise 403"""
def has_permission(self):
"""Check if this user has access to members or invited members for this portfolio.
The user is in self.request.user and the portfolio can be looked
up from the portfolio's primary key in self.kwargs["pk"]"""
portfolio = self.request.session.get("portfolio")
if not self.request.user.has_view_members_portfolio_permission(
portfolio
) and not self.request.user.has_edit_members_portfolio_permission(portfolio):
return False
return super().has_permission()
class PortfolioMemberEditPermission(PortfolioBasePermission):
"""Permission mixin that allows access to portfolio member or invited member pages if user
has access to edit, otherwise 403"""
def has_permission(self):
"""Check if this user has access to members or invited members for this portfolio.
The user is in self.request.user and the portfolio can be looked
up from the portfolio's primary key in self.kwargs["pk"]"""
portfolio = self.request.session.get("portfolio")
if not self.request.user.has_edit_members_portfolio_permission(portfolio):
return False
return super().has_permission()
class PortfolioMemberDomainsPermission(PortfolioBasePermission):
"""Permission mixin that allows access to portfolio member or invited member domains pages if user
has access to edit, otherwise 403"""
def has_permission(self):
"""Check if this user has access to member or invited member domains for this portfolio.
The user is in self.request.user and the portfolio can be looked
up from the portfolio's primary key in self.kwargs["pk"]"""
portfolio = self.request.session.get("portfolio")
if not self.request.user.has_view_members_portfolio_permission(
portfolio
) and not self.request.user.has_edit_members_portfolio_permission(portfolio):
return False
return super().has_permission()
class PortfolioMemberDomainsEditPermission(PortfolioBasePermission):
"""Permission mixin that allows access to portfolio member or invited member domains edit pages if user
has access to edit, otherwise 403"""
def has_permission(self):
"""Check if this user has access to member or invited member domains for this portfolio.
The user is in self.request.user and the portfolio can be looked
up from the portfolio's primary key in self.kwargs["pk"]"""
portfolio = self.request.session.get("portfolio")
if not self.request.user.has_edit_members_portfolio_permission(portfolio):
return False
return super().has_permission()

View file

@ -1,291 +0,0 @@
"""View classes that enforce authorization."""
import abc # abstract base class
from django.views.generic import DetailView, DeleteView, TemplateView, UpdateView
from registrar.models import Domain, DomainRequest, DomainInvitation, Portfolio
from registrar.models.user import User
from registrar.models.user_domain_role import UserDomainRole
from .mixins import (
DomainPermission,
DomainRequestPermission,
DomainRequestPermissionWithdraw,
DomainInvitationPermission,
DomainRequestWizardPermission,
PortfolioDomainRequestsPermission,
PortfolioDomainsPermission,
PortfolioMemberDomainsPermission,
PortfolioMemberDomainsEditPermission,
PortfolioMemberEditPermission,
UserDeleteDomainRolePermission,
UserProfilePermission,
PortfolioBasePermission,
PortfolioMembersPermission,
PortfolioMemberPermission,
DomainRequestPortfolioViewonlyPermission,
)
import logging
logger = logging.getLogger(__name__)
class DomainPermissionView(DomainPermission, DetailView, abc.ABC):
"""Abstract base view for domains that enforces permissions.
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""
# DetailView property for what model this is viewing
model = Domain
# variable name in template context for the model object
context_object_name = "domain"
# Adds context information for user permissions
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user = self.request.user
context["is_analyst_or_superuser"] = user.has_perm("registrar.analyst_access_permission") or user.has_perm(
"registrar.full_access_permission"
)
context["is_domain_manager"] = UserDomainRole.objects.filter(user=user, domain=self.object).exists()
context["is_portfolio_user"] = self.can_access_domain_via_portfolio(self.object.pk)
context["is_editable"] = self.is_editable()
# Stored in a variable for the linter
action = "analyst_action"
action_location = "analyst_action_location"
# Flag to see if an analyst is attempting to make edits
if action in self.request.session:
context[action] = self.request.session[action]
if action_location in self.request.session:
context[action_location] = self.request.session[action_location]
return context
def is_editable(self):
"""Returns whether domain is editable in the context of the view"""
domain_editable = self.object.is_editable()
if not domain_editable:
return False
# if user is domain manager or analyst or admin, return True
if (
self.can_access_other_user_domains(self.object.id)
or UserDomainRole.objects.filter(user=self.request.user, domain=self.object).exists()
):
return True
return False
# Abstract property enforces NotImplementedError on an attribute.
@property
@abc.abstractmethod
def template_name(self):
raise NotImplementedError
class DomainRequestPermissionView(DomainRequestPermission, DetailView, abc.ABC):
"""Abstract base view for domain requests that enforces permissions
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""
# DetailView property for what model this is viewing
model = DomainRequest
# variable name in template context for the model object
context_object_name = "DomainRequest"
# Abstract property enforces NotImplementedError on an attribute.
@property
@abc.abstractmethod
def template_name(self):
raise NotImplementedError
class DomainRequestPortfolioViewonlyView(DomainRequestPortfolioViewonlyPermission, DetailView, abc.ABC):
"""Abstract base view for domain requests that enforces permissions
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""
# DetailView property for what model this is viewing
model = DomainRequest
# variable name in template context for the model object
context_object_name = "DomainRequest"
# Abstract property enforces NotImplementedError on an attribute.
@property
@abc.abstractmethod
def template_name(self):
raise NotImplementedError
class DomainRequestPermissionWithdrawView(DomainRequestPermissionWithdraw, DetailView, abc.ABC):
"""Abstract base view for domain request withdraw function
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""
# DetailView property for what model this is viewing
model = DomainRequest
# variable name in template context for the model object
context_object_name = "DomainRequest"
# Abstract property enforces NotImplementedError on an attribute.
@property
@abc.abstractmethod
def template_name(self):
raise NotImplementedError
class DomainRequestWizardPermissionView(DomainRequestWizardPermission, TemplateView, abc.ABC):
"""Abstract base view for the domain request form that enforces permissions
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""
# Abstract property enforces NotImplementedError on an attribute.
@property
@abc.abstractmethod
def template_name(self):
raise NotImplementedError
class DomainInvitationPermissionCancelView(DomainInvitationPermission, UpdateView, abc.ABC):
"""Abstract view for cancelling a DomainInvitation."""
model = DomainInvitation
object: DomainInvitation
class DomainRequestPermissionDeleteView(DomainRequestPermission, DeleteView, abc.ABC):
"""Abstract view for deleting a DomainRequest."""
model = DomainRequest
object: DomainRequest
class UserDomainRolePermissionDeleteView(UserDeleteDomainRolePermission, DeleteView, abc.ABC):
"""Abstract base view for deleting a UserDomainRole.
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""
# DetailView property for what model this is viewing
model = UserDomainRole
# workaround for type mismatch in DeleteView
object: UserDomainRole
# variable name in template context for the model object
context_object_name = "userdomainrole"
class UserProfilePermissionView(UserProfilePermission, DetailView, abc.ABC):
"""Abstract base view for user profile view that enforces permissions.
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""
# DetailView property for what model this is viewing
model = User
# variable name in template context for the model object
context_object_name = "user"
# Abstract property enforces NotImplementedError on an attribute.
@property
@abc.abstractmethod
def template_name(self):
raise NotImplementedError
class PortfolioBasePermissionView(PortfolioBasePermission, DetailView, abc.ABC):
"""Abstract base view for portfolio views that enforces permissions.
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""
# DetailView property for what model this is viewing
model = Portfolio
# variable name in template context for the model object
context_object_name = "portfolio"
# Abstract property enforces NotImplementedError on an attribute.
@property
@abc.abstractmethod
def template_name(self):
raise NotImplementedError
class PortfolioDomainsPermissionView(PortfolioDomainsPermission, PortfolioBasePermissionView, abc.ABC):
"""Abstract base view for portfolio domains views that enforces permissions.
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""
class NoPortfolioDomainsPermissionView(PortfolioBasePermissionView, abc.ABC):
"""Abstract base view for a user without access to the
portfolio domains views that enforces permissions.
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""
class PortfolioDomainRequestsPermissionView(PortfolioDomainRequestsPermission, PortfolioBasePermissionView, abc.ABC):
"""Abstract base view for portfolio domain request views that enforces permissions.
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""
class PortfolioMembersPermissionView(PortfolioMembersPermission, PortfolioBasePermissionView, abc.ABC):
"""Abstract base view for portfolio members views that enforces permissions.
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""
class PortfolioMemberPermissionView(PortfolioMemberPermission, PortfolioBasePermissionView, abc.ABC):
"""Abstract base view for portfolio member views that enforces permissions.
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""
class PortfolioMemberEditPermissionView(PortfolioMemberEditPermission, PortfolioBasePermissionView, abc.ABC):
"""Abstract base view for portfolio member edit views that enforces permissions.
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""
class PortfolioMemberDomainsPermissionView(PortfolioMemberDomainsPermission, PortfolioBasePermissionView, abc.ABC):
"""Abstract base view for portfolio member domains views that enforces permissions.
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""
class PortfolioMemberDomainsEditPermissionView(
PortfolioMemberDomainsEditPermission, PortfolioBasePermissionView, abc.ABC
):
"""Abstract base view for portfolio member domains edit views that enforces permissions.
This abstract view cannot be instantiated. Actual views must specify
`template_name`.
"""