added decorators for domain requests, ignored __debug, update domain request pks

This commit is contained in:
David Kennedy 2025-02-11 18:35:38 -05:00
parent bd071a0fb3
commit 6269cc56e3
No known key found for this signature in database
GPG key ID: 6528A5386E66B96B
15 changed files with 112 additions and 289 deletions

View file

@ -68,7 +68,7 @@ for step, view in [
(PortfolioDomainRequestStep.REQUESTING_ENTITY, views.RequestingEntity), (PortfolioDomainRequestStep.REQUESTING_ENTITY, views.RequestingEntity),
(PortfolioDomainRequestStep.ADDITIONAL_DETAILS, views.PortfolioAdditionalDetails), (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 = [ urlpatterns = [
@ -260,27 +260,27 @@ urlpatterns = [
name="export_data_type_user", name="export_data_type_user",
), ),
path( path(
"domain-request/<int:id>/edit/", "domain-request/<int:domain_request_pk>/edit/",
views.DomainRequestWizard.as_view(), views.DomainRequestWizard.as_view(),
name=views.DomainRequestWizard.EDIT_URL_NAME, name=views.DomainRequestWizard.EDIT_URL_NAME,
), ),
path( path(
"domain-request/<int:pk>", "domain-request/<int:domain_request_pk>",
views.DomainRequestStatus.as_view(), views.DomainRequestStatus.as_view(),
name="domain-request-status", name="domain-request-status",
), ),
path( path(
"domain-request/viewonly/<int:pk>", "domain-request/viewonly/<int:domain_request_pk>",
views.PortfolioDomainRequestStatusViewOnly.as_view(), views.PortfolioDomainRequestStatusViewOnly.as_view(),
name="domain-request-status-viewonly", name="domain-request-status-viewonly",
), ),
path( path(
"domain-request/<int:pk>/withdraw", "domain-request/<int:domain_request_pk>/withdraw",
views.DomainRequestWithdrawConfirmation.as_view(), views.DomainRequestWithdrawConfirmation.as_view(),
name="domain-request-withdraw-confirmation", name="domain-request-withdraw-confirmation",
), ),
path( path(
"domain-request/<int:pk>/withdrawconfirmed", "domain-request/<int:domain_request_pk>/withdrawconfirmed",
views.DomainRequestWithdrawn.as_view(), views.DomainRequestWithdrawn.as_view(),
name="domain-request-withdrawn", name="domain-request-withdrawn",
), ),
@ -369,7 +369,7 @@ urlpatterns = [
name="invitation-cancel", name="invitation-cancel",
), ),
path( path(
"domain-request/<int:pk>/delete", "domain-request/<int:domain_request_pk>/delete",
views.DomainRequestDeleteView.as_view(http_method_names=["post"]), views.DomainRequestDeleteView.as_view(http_method_names=["post"]),
name="domain-request-delete", name="domain-request-delete",
), ),

View file

@ -8,10 +8,14 @@ ALL = "all"
IS_SUPERUSER = "is_superuser" IS_SUPERUSER = "is_superuser"
IS_STAFF = "is_staff" IS_STAFF = "is_staff"
IS_DOMAIN_MANAGER = "is_domain_manager" IS_DOMAIN_MANAGER = "is_domain_manager"
IS_DOMAIN_REQUEST_CREATOR = "is_domain_request_creator"
IS_STAFF_MANAGING_DOMAIN = "is_staff_managing_domain" IS_STAFF_MANAGING_DOMAIN = "is_staff_managing_domain"
IS_PORTFOLIO_MEMBER_AND_DOMAIN_MANAGER = "is_portfolio_member_and_domain_manager" 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" IS_DOMAIN_MANAGER_AND_NOT_PORTFOLIO_MEMBER = "is_domain_manager_and_not_portfolio_member"
HAS_PORTFOLIO_DOMAINS_VIEW_ALL = "has_portfolio_domains_view_all" 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_DOMAINS_VIEW_MANAGED = "has_portfolio_domains_view_managed" # HAS_PORTFOLIO_DOMAINS_VIEW_MANAGED = "has_portfolio_domains_view_managed"
@ -108,15 +112,52 @@ def _user_has_permission(user, request, rules, **kwargs):
has_permission = _is_domain_manager(user, domain_id) and not _is_portfolio_member(request) has_permission = _is_domain_manager(user, domain_id) and not _is_portfolio_member(request)
conditions_met.append(has_permission) conditions_met.append(has_permission)
if not any(conditions_met) and IS_DOMAIN_REQUEST_CREATOR in rules:
domain_request_id = kwargs.get("domain_request_pk")
has_permission = _is_domain_request_creator(user, domain_request_id)
conditions_met.append(has_permission)
if not any(conditions_met) and HAS_PORTFOLIO_DOMAIN_REQUESTS_ANY_PERM in rules:
has_permission = user.is_org_user(request) and user.has_any_requests_portfolio_permission(
request.session.get("portfolio")
)
conditions_met.append(has_permission)
if not any(conditions_met) and HAS_PORTFOLIO_DOMAIN_REQUESTS_VIEW_ALL in rules:
has_permission = user.is_org_user(request) and user.has_view_all_domain_requests_portfolio_permission(
request.session.get("portfolio")
)
conditions_met.append(has_permission)
if not any(conditions_met) and HAS_PORTFOLIO_DOMAIN_REQUESTS_EDIT in rules:
domain_request_id = kwargs.get("domain_request_pk")
has_permission = _has_portfolio_domain_requests_edit(user, request, domain_request_id)
print(has_permission)
conditions_met.append(has_permission)
return any(conditions_met) return any(conditions_met)
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, domain_pk): def _is_domain_manager(user, domain_pk):
"""Checks to see if the user is a domain manager of the """Checks to see if the user is a domain manager of the
domain with domain_pk.""" domain with domain_pk."""
return UserDomainRole.objects.filter(user=user, domain_id=domain_pk).exists() return UserDomainRole.objects.filter(user=user, domain_id=domain_pk).exists()
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): def _is_portfolio_member(request):
"""Checks to see if the user in the request is a member of the """Checks to see if the user in the request is a member of the
portfolio in the request's session.""" portfolio in the request's session."""

View file

@ -184,6 +184,11 @@ class RestrictAccessMiddleware:
self.ignored_paths = [re.compile(pattern) for pattern in getattr(settings, "LOGIN_REQUIRED_IGNORE_PATHS", [])] self.ignored_paths = [re.compile(pattern) for pattern in getattr(settings, "LOGIN_REQUIRED_IGNORE_PATHS", [])]
def __call__(self, request): def __call__(self, request):
# Allow Django Debug Toolbar requests
if request.path.startswith("/__debug__/"):
return self.get_response(request)
# Allow requests that match LOGIN_REQUIRED_IGNORE_PATHS # Allow requests that match LOGIN_REQUIRED_IGNORE_PATHS
if any(pattern.match(request.path) for pattern in self.ignored_paths): if any(pattern.match(request.path) for pattern in self.ignored_paths):
return self.get_response(request) return self.get_response(request)

View file

@ -36,7 +36,7 @@
</ol> </ol>
</nav> </nav>
{% elif steps.prev %} {% 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"> <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> <use xlink:href="{%static 'img/sprite.svg'%}#arrow_back"></use>
</svg><span class="margin-left-05">Previous step</span> </svg><span class="margin-left-05">Previous step</span>

View file

@ -15,7 +15,7 @@
</svg> </svg>
{% endif %} {% endif %}
{% 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 %} {% if this_step == steps.current %}
class="usa-current" class="usa-current"
{% else %} {% 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>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> <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' DomainRequest.id %}">Cancel</a></p> <a href="{% url 'domain-request-status' domain_request_pk=DomainRequest.id %}">Cancel</a></p>
</div> </div>
</div> </div>

View file

@ -4,7 +4,7 @@
{% for step in steps %} {% for step in steps %}
<section class="summary-item margin-top-3"> <section class="summary-item margin-top-3">
{% if is_editable %} {% 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 %} {% endif %}
{% if step == Step.REQUESTING_ENTITY %} {% if step == Step.REQUESTING_ENTITY %}

View file

@ -4,7 +4,7 @@
{% for step in steps %} {% for step in steps %}
<section class="summary-item margin-top-3"> <section class="summary-item margin-top-3">
{% if is_editable %} {% 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 %} {% endif %}
{% if step == Step.ORGANIZATION_TYPE %} {% if step == Step.ORGANIZATION_TYPE %}

View file

@ -114,7 +114,7 @@
{% block modify_request %} {% block modify_request %}
{% if DomainRequest.is_withdrawable %} {% 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> Withdraw request</a>
</p> </p>
{% endif %} {% endif %}

View file

@ -5,28 +5,27 @@ from django.shortcuts import redirect, render
from django.urls import resolve, reverse from django.urls import resolve, reverse
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.generic import TemplateView from django.views.generic import DeleteView, DetailView, TemplateView
from django.contrib import messages from django.contrib import messages
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 import domain_request_wizard as forms
from registrar.forms.utility.wizard_form_helper import request_step_list from registrar.forms.utility.wizard_form_helper import request_step_list
from registrar.models import DomainRequest from registrar.models import DomainRequest
from registrar.models.contact import Contact from registrar.models.contact import Contact
from registrar.models.user import User from registrar.models.user import User
from registrar.views.utility import StepsHelper from registrar.views.utility import StepsHelper
from registrar.views.utility.permission_views import DomainRequestPermissionDeleteView
from registrar.utility.enums import Step, PortfolioDomainRequestStep from registrar.utility.enums import Step, PortfolioDomainRequestStep
from .utility import (
DomainRequestPermissionView,
DomainRequestPermissionWithdrawView,
DomainRequestWizardPermissionView,
DomainRequestPortfolioViewonlyView,
)
logger = logging.getLogger(__name__) 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. A common set of methods and configuration.
@ -51,7 +50,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
# NB: this is included here for reference. Do not change it without # NB: this is included here for reference. Do not change it without
# also changing the many places it is hardcoded in the HTML templates # also changing the many places it is hardcoded in the HTML templates
URL_NAMESPACE = "domain-request" 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" EDIT_URL_NAME = "edit-domain-request"
NEW_URL_NAME = "start" NEW_URL_NAME = "start"
FINISHED_URL_NAME = "finished" FINISHED_URL_NAME = "finished"
@ -174,7 +173,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
def has_pk(self): def has_pk(self):
"""Does this wizard know about a DomainRequest database record?""" """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): def get_step_enum(self):
"""Determines which step enum we should use for the wizard""" """Determines which step enum we should use for the wizard"""
@ -209,11 +208,11 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
try: try:
self._domain_request = DomainRequest.objects.get( self._domain_request = DomainRequest.objects.get(
creator=creator, creator=creator,
pk=self.kwargs.get("id"), pk=self.kwargs.get("domain_request_pk"),
) )
return self._domain_request return self._domain_request
except DomainRequest.DoesNotExist: 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 a user is creating a request, we assume that perms are handled upstream
if self.request.user.is_org_user(self.request): if self.request.user.is_org_user(self.request):
@ -292,10 +291,10 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
current_url = resolve(request.path_info).url_name 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 # domain request they are trying to edit to this wizard instance
# and remove any prior wizard data from their session # 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 del self.storage
# if accessing this class directly, redirect to either to an acknowledgement # if accessing this class directly, redirect to either to an acknowledgement
@ -474,7 +473,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
def goto(self, step): def goto(self, step):
self.steps.current = 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): def goto_next_step(self):
"""Redirects to the next step.""" """Redirects to the next step."""
@ -823,23 +822,12 @@ class Finished(DomainRequestWizard):
return render(self.request, self.template_name, context) 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" template_name = "domain_request_status.html"
model = DomainRequest
def has_permission(self): pk_url_kwarg = "domain_request_pk"
""" context_object_name = "DomainRequest"
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
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
"""Context override to add a step list to the context""" """Context override to add a step list to the context"""
@ -854,19 +842,27 @@ class DomainRequestStatus(DomainRequestPermissionView):
return context return context
class DomainRequestWithdrawConfirmation(DomainRequestPermissionWithdrawView): @grant_access(IS_DOMAIN_REQUEST_CREATOR)
class DomainRequestWithdrawConfirmation(DetailView):
"""This page will ask user to confirm if they want to withdraw """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. `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)
class DomainRequestWithdrawn(DetailView):
# this view renders no template # this view renders no template
template_name = "" template_name = ""
model = DomainRequest
pk_url_kwarg = "domain_request_pk"
context_object_name = "DomainRequest"
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
"""View class that does the actual withdrawing. """View class that does the actual withdrawing.
@ -874,7 +870,7 @@ class DomainRequestWithdrawn(DomainRequestPermissionWithdrawView):
If user click on withdraw confirm button, this view updates the status If user click on withdraw confirm button, this view updates the status
to withdraw and send back to homepage. 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.withdraw()
domain_request.save() domain_request.save()
if self.request.user.is_org_user(self.request): if self.request.user.is_org_user(self.request):
@ -883,28 +879,22 @@ class DomainRequestWithdrawn(DomainRequestPermissionWithdrawView):
return HttpResponseRedirect(reverse("home")) return HttpResponseRedirect(reverse("home"))
class DomainRequestDeleteView(DomainRequestPermissionDeleteView): @grant_access(IS_DOMAIN_REQUEST_CREATOR, HAS_PORTFOLIO_DOMAIN_REQUESTS_EDIT)
class DomainRequestDeleteView(DeleteView):
"""Delete view for home that allows the end user to delete DomainRequests""" """Delete view for home that allows the end user to delete DomainRequests"""
object: DomainRequest # workaround for type mismatch in DeleteView object: DomainRequest # workaround for type mismatch in DeleteView
model = DomainRequest
pk_url_kwarg = "domain_request_pk"
def has_permission(self): def has_permission(self):
"""Custom override for has_permission to exclude all statuses, except WITHDRAWN and STARTED""" """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 status = self.get_object().status
valid_statuses = [DomainRequest.DomainRequestStatus.WITHDRAWN, DomainRequest.DomainRequestStatus.STARTED] valid_statuses = [DomainRequest.DomainRequestStatus.WITHDRAWN, DomainRequest.DomainRequestStatus.STARTED]
if status not in valid_statuses: if status not in valid_statuses:
return False return False
# Portfolio users cannot delete their requests if they aren't permissioned to do so
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 return True
def get_success_url(self): def get_success_url(self):
@ -989,8 +979,12 @@ class DomainRequestDeleteView(DomainRequestPermissionDeleteView):
# region Portfolio views # 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" 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): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)

View file

@ -155,9 +155,9 @@ def serialize_domain_request(request, domain_request, user):
# Map the action label to corresponding URLs and icons # Map the action label to corresponding URLs and icons
action_url_map = { action_url_map = {
"Edit": reverse("edit-domain-request", kwargs={"id": domain_request.id}), "Edit": reverse("edit-domain-request", kwargs={"domain_request_pk": domain_request.id}),
"Manage": reverse("domain-request-status", kwargs={"pk": domain_request.id}), "Manage": reverse("domain-request-status", kwargs={"domain_request_pk": domain_request.id}),
"View": reverse("domain-request-status-viewonly", kwargs={"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"} svg_icon_map = {"Edit": "edit", "Manage": "settings", "View": "visibility"}

View file

@ -6,6 +6,7 @@ from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse from django.urls import reverse
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.contrib import messages from django.contrib import messages
from registrar.decorators import HAS_PORTFOLIO_DOMAIN_REQUESTS_ANY_PERM, grant_access
from registrar.forms import portfolio as portfolioForms from registrar.forms import portfolio as portfolioForms
from registrar.models import Portfolio, User from registrar.models import Portfolio, User
from registrar.models.domain import Domain from registrar.models.domain import Domain
@ -25,7 +26,6 @@ from registrar.utility.errors import MissingEmailError
from registrar.utility.enums import DefaultUserValues from registrar.utility.enums import DefaultUserValues
from registrar.views.utility.mixins import PortfolioMemberPermission from registrar.views.utility.mixins import PortfolioMemberPermission
from registrar.views.utility.permission_views import ( from registrar.views.utility.permission_views import (
PortfolioDomainRequestsPermissionView,
PortfolioDomainsPermissionView, PortfolioDomainsPermissionView,
PortfolioBasePermissionView, PortfolioBasePermissionView,
NoPortfolioDomainsPermissionView, NoPortfolioDomainsPermissionView,
@ -58,7 +58,8 @@ class PortfolioDomainsView(PortfolioDomainsPermissionView, View):
return render(request, "portfolio_domains.html", context) 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" template_name = "portfolio_requests.html"

View file

@ -3,11 +3,7 @@ from .always_404 import always_404
from .permission_views import ( from .permission_views import (
DomainPermissionView, DomainPermissionView,
DomainRequestPermissionView,
DomainRequestPermissionWithdrawView,
DomainRequestWizardPermissionView,
PortfolioMembersPermission, PortfolioMembersPermission,
DomainRequestPortfolioViewonlyView,
DomainInvitationPermissionCancelView, DomainInvitationPermissionCancelView,
) )
from .api_views import get_senior_official_from_federal_agency_json from .api_views import get_senior_official_from_federal_agency_json

View file

@ -286,51 +286,6 @@ class DomainPermission(PermissionsLoginMixin):
return True 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): class UserDeleteDomainRolePermission(PermissionsLoginMixin):
"""Permission mixin for UserDomainRole if user """Permission mixin for UserDomainRole if user
has access, otherwise 403""" has access, otherwise 403"""
@ -365,67 +320,6 @@ class UserDeleteDomainRolePermission(PermissionsLoginMixin):
return True 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): class DomainInvitationPermission(PermissionsLoginMixin):
"""Permission mixin that redirects to domain invitation if user has """Permission mixin that redirects to domain invitation if user has
access, otherwise 403" access, otherwise 403"
@ -496,23 +390,6 @@ class PortfolioDomainsPermission(PortfolioBasePermission):
return super().has_permission() 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): class PortfolioMembersPermission(PortfolioBasePermission):
"""Permission mixin that allows access to portfolio members pages if user """Permission mixin that allows access to portfolio members pages if user
has access, otherwise 403""" has access, otherwise 403"""

View file

@ -2,18 +2,14 @@
import abc # abstract base class import abc # abstract base class
from django.views.generic import DetailView, DeleteView, TemplateView, UpdateView from django.views.generic import DetailView, DeleteView, UpdateView
from registrar.models import Domain, DomainRequest, DomainInvitation, Portfolio from registrar.models import Domain, DomainInvitation, Portfolio
from registrar.models.user import User from registrar.models.user import User
from registrar.models.user_domain_role import UserDomainRole from registrar.models.user_domain_role import UserDomainRole
from .mixins import ( from .mixins import (
DomainPermission, DomainPermission,
DomainRequestPermission,
DomainRequestPermissionWithdraw,
DomainInvitationPermission, DomainInvitationPermission,
DomainRequestWizardPermission,
PortfolioDomainRequestsPermission,
PortfolioDomainsPermission, PortfolioDomainsPermission,
PortfolioMemberDomainsPermission, PortfolioMemberDomainsPermission,
PortfolioMemberDomainsEditPermission, PortfolioMemberDomainsEditPermission,
@ -23,7 +19,6 @@ from .mixins import (
PortfolioBasePermission, PortfolioBasePermission,
PortfolioMembersPermission, PortfolioMembersPermission,
PortfolioMemberPermission, PortfolioMemberPermission,
DomainRequestPortfolioViewonlyPermission,
) )
import logging import logging
@ -86,77 +81,6 @@ class DomainPermissionView(DomainPermission, DetailView, abc.ABC):
raise NotImplementedError 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): class DomainInvitationPermissionCancelView(DomainInvitationPermission, UpdateView, abc.ABC):
"""Abstract view for cancelling a DomainInvitation.""" """Abstract view for cancelling a DomainInvitation."""
@ -164,13 +88,6 @@ class DomainInvitationPermissionCancelView(DomainInvitationPermission, UpdateVie
object: 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): class UserDomainRolePermissionDeleteView(UserDeleteDomainRolePermission, DeleteView, abc.ABC):
"""Abstract base view for deleting a UserDomainRole. """Abstract base view for deleting a UserDomainRole.
@ -242,14 +159,6 @@ class NoPortfolioDomainsPermissionView(PortfolioBasePermissionView, abc.ABC):
""" """
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): class PortfolioMembersPermissionView(PortfolioMembersPermission, PortfolioBasePermissionView, abc.ABC):
"""Abstract base view for portfolio members views that enforces permissions. """Abstract base view for portfolio members views that enforces permissions.