This commit is contained in:
David Kennedy 2025-02-11 10:06:24 -05:00
parent d84aa421d9
commit a334f7dc3e
No known key found for this signature in database
GPG key ID: 6528A5386E66B96B
19 changed files with 216 additions and 131 deletions

View file

@ -296,56 +296,56 @@ 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: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:pk>/dns",
"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",
),
@ -370,7 +370,7 @@ urlpatterns = [
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 +392,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

View file

@ -1,15 +1,14 @@
from functools import wraps
from django.http import JsonResponse
from django.core.exceptions import ObjectDoesNotExist
from registrar.models.domain import Domain
from registrar.models.user_domain_role import UserDomainRole
import functools
from django.core.exceptions import PermissionDenied
from django.utils.decorators import method_decorator
from registrar.models import DomainInformation, DomainRequest, UserDomainRole
# Constants for clarity
ALL = "all"
IS_SUPERUSER = "is_superuser"
IS_STAFF = "is_staff"
IS_DOMAIN_MANAGER = "is_domain_manager"
IS_STAFF_MANAGING_DOMAIN = "is_staff_managing_domain"
def grant_access(*rules):
"""
@ -20,53 +19,128 @@ def grant_access(*rules):
@grant_access(IS_DOMAIN_MANAGER)
"""
def decorator(view_func):
view_func.has_explicit_access = True # Mark as explicitly access-controlled
existing_rules = getattr(view_func, "_access_rules", set())
existing_rules.update(rules) # Support multiple rules in one call
view_func._access_rules = existing_rules # Store rules on the function
def decorator(view):
if isinstance(view, type): # If decorating a class-based view (CBV)
original_dispatch = view.dispatch # save original dispatch method
@wraps(view_func)
def wrapper(request, *args, **kwargs):
user = request.user
# Skip authentication if @login_not_required is applied
if getattr(view_func, "login_not_required", False):
return view_func(request, *args, **kwargs)
# Allow everyone if `ALL` is in rules
if ALL in view_func._access_rules:
return view_func(request, *args, **kwargs)
@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
return original_dispatch(self, request, *args, **kwargs)
# Ensure user is authenticated
if not user.is_authenticated:
return JsonResponse({"error": "Authentication required"}, status=403)
view.dispatch = wrapped_dispatch # replace dispatch with wrapped version
return view
conditions_met = []
else: # If decorating a function-based view (FBV)
view.has_explicit_access = True
existing_rules = getattr(view, "_access_rules", set())
existing_rules.update(rules)
view._access_rules = existing_rules
if IS_STAFF in view_func._access_rules:
conditions_met.append(user.is_staff)
if not any(conditions_met) and IS_SUPERUSER in view_func._access_rules:
conditions_met.append(user.is_superuser)
if not any(conditions_met) and IS_DOMAIN_MANAGER in view_func._access_rules:
domain_id = kwargs.get('pk') or kwargs.get('domain_id')
if not domain_id:
return JsonResponse({"error": "Domain ID missing"}, status=400)
try:
domain = Domain.objects.get(pk=domain_id)
has_permission = UserDomainRole.objects.filter(
user=user, domain=domain
).exists()
conditions_met.append(has_permission)
except ObjectDoesNotExist:
return JsonResponse({"error": "Invalid Domain"}, status=404)
if not any(conditions_met):
return JsonResponse({"error": "Access Denied"}, status=403)
return view_func(request, *args, **kwargs)
return wrapper
@functools.wraps(view)
def wrapper(request, *args, **kwargs):
if not _user_has_permission(request.user, request, rules, **kwargs):
raise PermissionDenied
return view(request, *args, **kwargs)
return wrapper
return decorator
def _user_has_permission(user, request, rules, **kwargs):
"""
Checks if the user meets the permission requirements.
"""
# 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
if not user.is_authenticated:
return False
conditions_met = []
if IS_STAFF in rules:
conditions_met.append(user.is_staff)
if not any(conditions_met) and IS_SUPERUSER in rules:
conditions_met.append(user.is_superuser)
if not any(conditions_met) and IS_DOMAIN_MANAGER in rules:
domain_id = kwargs.get('domain_pk')
# Check UserDomainRole directly instead of fetching Domain
has_permission = UserDomainRole.objects.filter(user=user, domain_id=domain_id).exists()
conditions_met.append(has_permission)
if not any(conditions_met) and IS_STAFF_MANAGING_DOMAIN in rules:
domain_id = kwargs.get('domain_pk')
has_permission = _can_access_other_user_domains(request, domain_id)
conditions_met.append(has_permission)
return any(conditions_met)
def _can_access_other_user_domains(request, domain_pk):
"""Checks to see if an authorized user (staff or superuser)
can access a domain that they did not create or were invited to.
"""
# 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_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 = DomainInformation.objects.filter(domain_id=domain_pk).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

View file

@ -6,9 +6,9 @@ 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.http import JsonResponse
from django.urls import resolve
from registrar.models import User
from waffle.decorators import flag_is_active
@ -182,7 +182,7 @@ class RestrictAccessMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.ignored_paths = [re.compile(pattern) for pattern in getattr(settings, "LOGIN_REQUIRED_IGNORE_PATHS", [])]
def __call__(self, request):
# Allow requests that match LOGIN_REQUIRED_IGNORE_PATHS
if any(pattern.match(request.path) for pattern in self.ignored_paths):
@ -194,7 +194,7 @@ class RestrictAccessMiddleware:
view_func = resolver_match.func
app_name = resolver_match.app_name # Get app name of resolved view
except Exception:
return JsonResponse({"error": "Not Found"}, status=404)
return self.get_response(request)
# Auto-allow Django's built-in admin views (but NOT custom /admin/* views)
if app_name == "admin":
@ -206,6 +206,6 @@ class RestrictAccessMiddleware:
# Enforce explicit access fules for other views
if not getattr(view_func, "has_explicit_access", False):
return JsonResponse({"error": "Access Denied"}, status=403)
raise PermissionDenied
return self.get_response(request)

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 has_domain_renewal_flag and 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 has_domain_renewal_flag and 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 has_domain_renewal_flag and 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

@ -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>

View file

@ -15,6 +15,7 @@ from django.shortcuts import redirect, render, get_object_or_404
from django.urls import reverse
from django.views.generic.edit import FormMixin
from django.conf import settings
from registrar.decorators import IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN, grant_access
from registrar.forms.domain import DomainSuborganizationForm, DomainRenewalForm
from registrar.models import (
Domain,
@ -95,7 +96,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,7 +109,7 @@ 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
@ -256,7 +257,7 @@ class DomainFormBaseView(DomainBaseView, FormMixin):
exc_info=True,
)
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainView(DomainBaseView):
"""Domain detail overview page."""
@ -310,7 +311,7 @@ class DomainView(DomainBaseView):
self.object = self.get_object()
self._update_session_with_domain()
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainRenewalView(DomainBaseView):
"""Domain detail overview page."""
@ -344,9 +345,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 +364,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, has_domain_renewal_flag, and is_editable for re-render
@ -379,7 +380,7 @@ class DomainRenewalView(DomainBaseView):
},
)
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainOrgNameAddressView(DomainFormBaseView):
"""Organization view"""
@ -396,7 +397,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."""
@ -455,7 +456,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."""
@ -494,7 +495,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."""
@ -587,7 +588,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."""
@ -764,7 +765,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."""
@ -885,7 +886,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."""
@ -936,7 +937,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."""
@ -1042,7 +1043,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."""
@ -1199,7 +1200,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."""
@ -1304,7 +1305,7 @@ 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}."
@ -1317,13 +1318,13 @@ class DomainDeleteUserView(UserDomainRolePermissionDeleteView):
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

@ -143,7 +143,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

@ -35,3 +35,11 @@ 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"""
print("this is called")
if context is None:
context = {}
return render(request, "404.html", context=context, status=404)

View file

@ -203,7 +203,7 @@ class DomainPermission(PermissionsLoginMixin):
"""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"]
up from the domain's primary key in self.kwargs["domain_pk"]
"""
if not self.request.user.is_authenticated:
@ -212,7 +212,7 @@ class DomainPermission(PermissionsLoginMixin):
if self.request.user.is_restricted():
return False
pk = self.kwargs["pk"]
pk = self.kwargs["domain_pk"]
# If pk is none then something went very wrong...
if pk is None:
raise ValueError("Primary key is None")

View file

@ -39,6 +39,7 @@ class DomainPermissionView(DomainPermission, DetailView, abc.ABC):
# DetailView property for what model this is viewing
model = Domain
pk_url_kwarg = "domain_pk"
# variable name in template context for the model object
context_object_name = "domain"