additional cleanup of permission views and mixins, handling of domain invitations and user domain roles in decorators

This commit is contained in:
David Kennedy 2025-02-12 08:16:16 -05:00
parent 40737cbcf7
commit e4021b76c1
No known key found for this signature in database
GPG key ID: 6528A5386E66B96B
7 changed files with 196 additions and 283 deletions

View file

@ -364,7 +364,7 @@ urlpatterns = [
name="user-profile", name="user-profile",
), ),
path( path(
"invitation/<int:pk>/cancel", "invitation/<int:domain_invitation_pk>/cancel",
views.DomainInvitationCancelView.as_view(http_method_names=["post"]), views.DomainInvitationCancelView.as_view(http_method_names=["post"]),
name="invitation-cancel", name="invitation-cancel",
), ),

View file

@ -1,7 +1,7 @@
import functools import functools
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from registrar.models import Domain, DomainInformation, DomainRequest, UserDomainRole from registrar.models import Domain, DomainInformation, DomainInvitation, DomainRequest, UserDomainRole
# Constants for clarity # Constants for clarity
ALL = "all" ALL = "all"
@ -18,7 +18,6 @@ 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_ANY_PERM = "has_portfolio_domain_requests_any_perm"
HAS_PORTFOLIO_DOMAIN_REQUESTS_VIEW_ALL = "has_portfolio_domain_requests_view_all" HAS_PORTFOLIO_DOMAIN_REQUESTS_VIEW_ALL = "has_portfolio_domain_requests_view_all"
HAS_PORTFOLIO_DOMAIN_REQUESTS_EDIT = "has_portfolio_domain_requests_edit" HAS_PORTFOLIO_DOMAIN_REQUESTS_EDIT = "has_portfolio_domain_requests_edit"
# HAS_PORTFOLIO_DOMAINS_VIEW_MANAGED = "has_portfolio_domains_view_managed"
def grant_access(*rules): def grant_access(*rules):
@ -90,13 +89,11 @@ def _user_has_permission(user, request, rules, **kwargs):
conditions_met.append(user.is_superuser) conditions_met.append(user.is_superuser)
if not any(conditions_met) and IS_DOMAIN_MANAGER in rules: if not any(conditions_met) and IS_DOMAIN_MANAGER in rules:
domain_id = kwargs.get("domain_pk") has_permission = _is_domain_manager(user, **kwargs)
has_permission = _is_domain_manager(user, domain_id)
conditions_met.append(has_permission) conditions_met.append(has_permission)
if not any(conditions_met) and IS_STAFF_MANAGING_DOMAIN in rules: if not any(conditions_met) and IS_STAFF_MANAGING_DOMAIN in rules:
domain_id = kwargs.get("domain_pk") has_permission = _is_staff_managing_domain(request, **kwargs)
has_permission = _can_access_other_user_domains(request, domain_id)
conditions_met.append(has_permission) conditions_met.append(has_permission)
if not any(conditions_met) and IS_PORTFOLIO_MEMBER in rules: if not any(conditions_met) and IS_PORTFOLIO_MEMBER in rules:
@ -115,13 +112,11 @@ def _user_has_permission(user, request, rules, **kwargs):
conditions_met.append(has_permission) conditions_met.append(has_permission)
if not any(conditions_met) and IS_PORTFOLIO_MEMBER_AND_DOMAIN_MANAGER in rules: if not any(conditions_met) and IS_PORTFOLIO_MEMBER_AND_DOMAIN_MANAGER in rules:
domain_id = kwargs.get("domain_pk") has_permission = _is_domain_manager(user, **kwargs) and _is_portfolio_member(request)
has_permission = _is_domain_manager(user, domain_id) and _is_portfolio_member(request)
conditions_met.append(has_permission) conditions_met.append(has_permission)
if not any(conditions_met) and IS_DOMAIN_MANAGER_AND_NOT_PORTFOLIO_MEMBER in rules: if not any(conditions_met) and IS_DOMAIN_MANAGER_AND_NOT_PORTFOLIO_MEMBER in rules:
domain_id = kwargs.get("domain_pk") has_permission = _is_domain_manager(user, **kwargs) 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: if not any(conditions_met) and IS_DOMAIN_REQUEST_CREATOR in rules:
@ -156,10 +151,24 @@ def _has_portfolio_domain_requests_edit(user, request, domain_request_id):
return user.is_org_user(request) and user.has_edit_request_portfolio_permission(request.session.get("portfolio")) 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, **kwargs):
"""Checks to see if the user is a domain manager of the """
domain with domain_pk.""" Determines if the given user is a domain manager for a specified domain.
return UserDomainRole.objects.filter(user=user, domain_id=domain_pk).exists()
- 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): def _is_domain_request_creator(user, domain_request_pk):
@ -176,10 +185,35 @@ def _is_portfolio_member(request):
return request.user.is_org_user(request) return request.user.is_org_user(request)
def _can_access_other_user_domains(request, domain_pk): def _is_staff_managing_domain(request, **kwargs):
"""Checks to see if an authorized user (staff or superuser)
can access a domain that they did not create or were invited to.
""" """
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... # Check if the request user is permissioned...
user_is_analyst_or_superuser = request.user.has_perm( user_is_analyst_or_superuser = request.user.has_perm(
@ -197,7 +231,7 @@ def _can_access_other_user_domains(request, domain_pk):
can_do_action = ( can_do_action = (
"analyst_action" in session "analyst_action" in session
and "analyst_action_location" in session and "analyst_action_location" in session
and session["analyst_action_location"] == domain_pk and session["analyst_action_location"] == domain_id
) )
if not can_do_action: if not can_do_action:
@ -215,7 +249,7 @@ def _can_access_other_user_domains(request, domain_pk):
None, None,
] ]
requested_domain = DomainInformation.objects.filter(domain_id=domain_pk).first() requested_domain = DomainInformation.objects.filter(domain_id=domain_id).first()
# if no domain information or domain request exist, the user # if no domain information or domain request exist, the user
# should be able to manage the domain; however, if domain information # should be able to manage the domain; however, if domain information

View file

@ -154,7 +154,7 @@
{% if not portfolio %}<td data-label="Status">{{ invitation.domain_invitation.status|title }}</td>{% endif %} {% if not portfolio %}<td data-label="Status">{{ invitation.domain_invitation.status|title }}</td>{% endif %}
<td> <td>
{% if invitation.domain_invitation.status == invitation.domain_invitation.DomainInvitationStatus.INVITED %} {% 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"> {% csrf_token %}<input type="submit" class="usa-button--unstyled text-no-underline cursor-pointer" value="Cancel">
</form> </form>
{% endif %} {% endif %}

View file

@ -1,10 +1,3 @@
"""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 from datetime import date
import logging import logging
import requests import requests
@ -13,7 +6,7 @@ from django.contrib.messages.views import SuccessMessageMixin
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import redirect, render, get_object_or_404 from django.shortcuts import redirect, render, get_object_or_404
from django.urls import reverse from django.urls import reverse
from django.views.generic import DeleteView from django.views.generic import DeleteView, DetailView, UpdateView
from django.views.generic.edit import FormMixin from django.views.generic.edit import FormMixin
from django.conf import settings from django.conf import settings
from registrar.decorators import ( from registrar.decorators import (
@ -49,7 +42,6 @@ from registrar.utility.errors import (
SecurityEmailErrorCodes, SecurityEmailErrorCodes,
) )
from registrar.models.utility.contact_error import ContactError 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.utility.waffle import flag_is_active_for_user
from registrar.views.utility.invitation_helper import ( from registrar.views.utility.invitation_helper import (
get_org_membership, get_org_membership,
@ -76,19 +68,22 @@ from epplibwrapper import (
from ..utility.email import send_templated_email, EmailSendingError from ..utility.email import send_templated_email, EmailSendingError
from ..utility.email_invitations import send_domain_invitation_email, send_portfolio_invitation_email from ..utility.email_invitations import send_domain_invitation_email, send_portfolio_invitation_email
from .utility import DomainPermissionView, DomainInvitationPermissionCancelView
from django import forms from django import forms
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class DomainBaseView(DomainPermissionView): class DomainBaseView(DetailView):
""" """
Base View for the Domain. Handles getting and setting the domain Base View for the Domain. Handles getting and setting the domain
in session cache on GETs. Also provides methods for getting in session cache on GETs. Also provides methods for getting
and setting the domain in cache and setting the domain in cache
""" """
model = Domain
pk_url_kwarg = "domain_pk"
context_object_name = "domain"
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self._get_domain(request) self._get_domain(request)
context = self.get_context_data(object=self.object) context = self.get_context_data(object=self.object)
@ -120,6 +115,134 @@ class DomainBaseView(DomainPermissionView):
domain_pk = "domain:" + str(self.kwargs.get("domain_pk")) domain_pk = "domain:" + str(self.kwargs.get("domain_pk"))
self.session[domain_pk] = self.object 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): class DomainFormBaseView(DomainBaseView, FormMixin):
""" """
@ -433,7 +556,7 @@ class DomainOrgNameAddressView(DomainFormBaseView):
return super().has_permission() return super().has_permission()
@grant_access(IS_PORTFOLIO_MEMBER_AND_DOMAIN_MANAGER) @grant_access(IS_PORTFOLIO_MEMBER_AND_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainSubOrganizationView(DomainFormBaseView): class DomainSubOrganizationView(DomainFormBaseView):
"""Suborganization view""" """Suborganization view"""
@ -480,7 +603,7 @@ class DomainSubOrganizationView(DomainFormBaseView):
return super().form_valid(form) return super().form_valid(form)
@grant_access(IS_DOMAIN_MANAGER_AND_NOT_PORTFOLIO_MEMBER) @grant_access(IS_DOMAIN_MANAGER_AND_NOT_PORTFOLIO_MEMBER, IS_STAFF_MANAGING_DOMAIN)
class DomainSeniorOfficialView(DomainFormBaseView): class DomainSeniorOfficialView(DomainFormBaseView):
"""Domain senior official editing view.""" """Domain senior official editing view."""
@ -1307,8 +1430,9 @@ class DomainAddUserView(DomainFormBaseView):
@grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN) @grant_access(IS_DOMAIN_MANAGER, IS_STAFF_MANAGING_DOMAIN)
class DomainInvitationCancelView(SuccessMessageMixin, DomainInvitationPermissionCancelView): class DomainInvitationCancelView(SuccessMessageMixin, UpdateView):
object: DomainInvitation model = DomainInvitation
pk_url_kwarg = "domain_invitation_pk"
fields = [] fields = []
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):

View file

@ -2,8 +2,6 @@ from .steps_helper import StepsHelper
from .always_404 import always_404 from .always_404 import always_404
from .permission_views import ( from .permission_views import (
DomainPermissionView,
PortfolioMembersPermission, PortfolioMembersPermission,
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

@ -1,14 +1,6 @@
"""Permissions-related mixin classes.""" """Permissions-related mixin classes."""
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from registrar.models import (
Domain,
DomainRequest,
DomainInvitation,
DomainInformation,
UserDomainRole,
)
import logging import logging
@ -195,151 +187,6 @@ class PortfolioReportsPermission(PermissionsLoginMixin):
return self.request.user.is_org_user(self.request) 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["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 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 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): class UserProfilePermission(PermissionsLoginMixin):
"""Permission mixin that redirects to user profile if user """Permission mixin that redirects to user profile 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, UpdateView from django.views.generic import DetailView
from registrar.models import Domain, DomainInvitation, Portfolio from registrar.models import Portfolio
from registrar.models.user import User from registrar.models.user import User
from registrar.models.user_domain_role import UserDomainRole
from .mixins import ( from .mixins import (
DomainPermission,
DomainInvitationPermission,
PortfolioMemberDomainsPermission, PortfolioMemberDomainsPermission,
PortfolioMemberDomainsEditPermission, PortfolioMemberDomainsEditPermission,
PortfolioMemberEditPermission, PortfolioMemberEditPermission,
UserDeleteDomainRolePermission,
UserProfilePermission, UserProfilePermission,
PortfolioBasePermission, PortfolioBasePermission,
PortfolioMembersPermission, PortfolioMembersPermission,
@ -24,92 +20,6 @@ import logging
logger = logging.getLogger(__name__) 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
pk_url_kwarg = "domain_pk"
# 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
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
# 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 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): class UserProfilePermissionView(UserProfilePermission, DetailView, abc.ABC):
"""Abstract base view for user profile view that enforces permissions. """Abstract base view for user profile view that enforces permissions.