filter requests based on permissions, updated actions based on permissions

This commit is contained in:
David Kennedy 2024-09-05 19:22:31 -04:00
parent ec7202b47c
commit faf57902f0
No known key found for this signature in database
GPG key ID: 6528A5386E66B96B
11 changed files with 92 additions and 94 deletions

View file

@ -66,20 +66,20 @@ def portfolio_permissions(request):
return {
"has_base_portfolio_permission": request.user.has_base_portfolio_permission(portfolio),
"has_domains_portfolio_permission": request.user.has_domains_portfolio_permission(portfolio),
"has_domain_requests_portfolio_permission": request.user.has_domain_requests_portfolio_permission(
"has_requests_portfolio_permission": request.user.has_requests_portfolio_permission(
portfolio
),
"has_view_suborganization": request.user.has_view_suborganization(portfolio),
"has_edit_suborganization": request.user.has_edit_suborganization(portfolio),
"has_view_suborganization_portfolio_permission": request.user.has_view_suborganization_portfolio_permission(portfolio),
"has_edit_suborganization_portfolio_permission": request.user.has_edit_suborganization_portfolio_permission(portfolio),
"portfolio": portfolio,
"has_organization_feature_flag": True,
}
return {
"has_base_portfolio_permission": False,
"has_domains_portfolio_permission": False,
"has_domain_requests_portfolio_permission": False,
"has_view_suborganization": False,
"has_edit_suborganization": False,
"has_requests_portfolio_permission": False,
"has_view_suborganization_portfolio_permission": False,
"has_edit_suborganization_portfolio_permission": False,
"portfolio": None,
"has_organization_feature_flag": False,
}
@ -89,9 +89,9 @@ def portfolio_permissions(request):
return {
"has_base_portfolio_permission": False,
"has_domains_portfolio_permission": False,
"has_domain_requests_portfolio_permission": False,
"has_view_suborganization": False,
"has_edit_suborganization": False,
"has_requests_portfolio_permission": False,
"has_view_suborganization_portfolio_permission": False,
"has_edit_suborganization_portfolio_permission": False,
"portfolio": None,
"has_organization_feature_flag": False,
}

View file

@ -223,20 +223,27 @@ class User(AbstractUser):
portfolio, UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS
) or self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS)
def has_domain_requests_portfolio_permission(self, portfolio):
return self._has_portfolio_permission(
portfolio, UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS
) or self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.VIEW_CREATED_REQUESTS)
def has_view_all_domains_permission(self, portfolio):
def has_view_all_domains_portfolio_permission(self, portfolio):
"""Determines if the current user can view all available domains in a given portfolio"""
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS)
def has_requests_portfolio_permission(self, portfolio):
return self._has_portfolio_permission(
portfolio, UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS
) or self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.EDIT_REQUESTS)
def has_view_all_requests_portfolio_permission(self, portfolio):
"""Determines if the current user can view all available domain requests in a given portfolio"""
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS)
def has_edit_request_portfolio_permission(self, portfolio):
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.EDIT_REQUESTS)
# Field specific permission checks
def has_view_suborganization(self, portfolio):
def has_view_suborganization_portfolio_permission(self, portfolio):
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.VIEW_SUBORGANIZATION)
def has_edit_suborganization(self, portfolio):
def has_edit_suborganization_portfolio_permission(self, portfolio):
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.EDIT_SUBORGANIZATION)
def get_first_portfolio(self):
@ -245,34 +252,31 @@ class User(AbstractUser):
return permission.portfolio
return None
def has_edit_requests(self, portfolio):
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.EDIT_REQUESTS)
def portfolio_role_summary(self, portfolio):
"""Returns a list of roles based on the user's permissions."""
roles = []
# Define the conditions and their corresponding roles
conditions_roles = [
(self.has_edit_suborganization(portfolio), ["Admin"]),
(self.has_edit_suborganization_portfolio_permission(portfolio), ["Admin"]),
(
self.has_view_all_domains_permission(portfolio)
and self.has_domain_requests_portfolio_permission(portfolio)
and self.has_edit_requests(portfolio),
self.has_view_all_domains_portfolio_permission(portfolio)
and self.has_requests_portfolio_permission(portfolio)
and self.has_edit_request_portfolio_permission(portfolio),
["View-only admin", "Domain requestor"],
),
(
self.has_view_all_domains_permission(portfolio)
and self.has_domain_requests_portfolio_permission(portfolio),
self.has_view_all_domains_portfolio_permission(portfolio)
and self.has_requests_portfolio_permission(portfolio),
["View-only admin"],
),
(
self.has_base_portfolio_permission(portfolio)
and self.has_edit_requests(portfolio)
and self.has_edit_request_portfolio_permission(portfolio)
and self.has_domains_portfolio_permission(portfolio),
["Domain requestor", "Domain manager"],
),
(self.has_base_portfolio_permission(portfolio) and self.has_edit_requests(portfolio), ["Domain requestor"]),
(self.has_base_portfolio_permission(portfolio) and self.has_edit_request_portfolio_permission(portfolio), ["Domain requestor"]),
(
self.has_base_portfolio_permission(portfolio) and self.has_domains_portfolio_permission(portfolio),
["Domain manager"],
@ -443,7 +447,7 @@ class User(AbstractUser):
def get_user_domain_ids(self, request):
"""Returns either the domains ids associated with this user on UserDomainRole or Portfolio"""
portfolio = request.session.get("portfolio")
if self.is_org_user(request) and self.has_view_all_domains_permission(portfolio):
if self.is_org_user(request) and self.has_view_all_domains_portfolio_permission(portfolio):
return DomainInformation.objects.filter(portfolio=portfolio).values_list("domain_id", flat=True)
else:
return UserDomainRole.objects.filter(user=self).values_list("domain_id", flat=True)

View file

@ -21,7 +21,6 @@ class UserPortfolioPermissionChoices(models.TextChoices):
EDIT_MEMBER = "edit_member", "Create and edit members"
VIEW_ALL_REQUESTS = "view_all_requests", "View all requests"
VIEW_CREATED_REQUESTS = "view_created_requests", "View created requests"
EDIT_REQUESTS = "edit_requests", "Create and edit requests"
VIEW_PORTFOLIO = "view_portfolio", "View organization"

View file

@ -72,9 +72,9 @@
{% include "includes/summary_item.html" with title='DNSSEC' value='Not Enabled' edit_link=url editable=is_editable %}
{% endif %}
{% if portfolio and has_domains_portfolio_permission and has_view_suborganization %}
{% if portfolio and has_domains_portfolio_permission and has_view_suborganization_portfolio_permission %}
{% url 'domain-suborganization' 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_suborganization %}
{% include "includes/summary_item.html" with title='Suborganization' value=domain.domain_info.sub_organization edit_link=url editable=is_editable|and:has_edit_suborganization_portfolio_permission %}
{% else %}
{% url 'domain-org-name-address' pk=domain.id as url %}
{% include "includes/summary_item.html" with title='Organization name and mailing address' value=domain.domain_info address='true' edit_link=url editable=is_editable %}

View file

@ -61,7 +61,7 @@
{% if portfolio %}
{% comment %} Only show this menu option if the user has the perms to do so {% endcomment %}
{% if has_domains_portfolio_permission and has_view_suborganization %}
{% if has_domains_portfolio_permission and has_view_suborganization_portfolio_permission %}
{% with url_name="domain-suborganization" %}
{% include "includes/domain_sidenav_item.html" with item_text="Suborganization" %}
{% endwith %}

View file

@ -15,7 +15,7 @@
If you believe there is an error please contact <a href="mailto:help@get.gov" class="usa-link">help@get.gov</a>.
</p>
{% if has_domains_portfolio_permission and has_edit_suborganization %}
{% if has_domains_portfolio_permission and has_edit_suborganization_portfolio_permission %}
<form class="usa-form usa-form--large" method="post" novalidate id="form-container">
{% csrf_token %}
{% input_with_errors form.sub_organization %}

View file

@ -156,7 +156,7 @@
<th data-sortable="name" scope="col" role="columnheader">Domain name</th>
<th data-sortable="expiration_date" scope="col" role="columnheader">Expires</th>
<th data-sortable="state_display" scope="col" role="columnheader">Status</th>
{% if portfolio and has_view_suborganization %}
{% if portfolio and has_view_suborganization_portfolio_permission %}
<th data-sortable="suborganization" scope="col" role="columnheader">Suborganization</th>
{% endif %}
<th

View file

@ -53,7 +53,7 @@
</li>
<li class="usa-nav__primary-item">
{% if has_domain_requests_portfolio_permission %}
{% if has_requests_portfolio_permission %}
{% url 'domain-requests' as url %}
<button
type="button"

View file

@ -1326,16 +1326,16 @@ class TestUser(TestCase):
User.objects.all().delete()
UserDomainRole.objects.all().delete()
@patch.object(User, "has_edit_suborganization", return_value=True)
@patch.object(User, "has_edit_suborganization_portfolio_permission", return_value=True)
def test_portfolio_role_summary_admin(self, mock_edit_suborganization):
# Test if the user is recognized as an Admin
self.assertEqual(self.user.portfolio_role_summary(self.portfolio), ["Admin"])
@patch.multiple(
User,
has_view_all_domains_permission=lambda self, portfolio: True,
has_domain_requests_portfolio_permission=lambda self, portfolio: True,
has_edit_requests=lambda self, portfolio: True,
has_view_all_domains_portfolio_permission=lambda self, portfolio: True,
has_requests_portfolio_permission=lambda self, portfolio: True,
has_edit_request_portfolio_permission=lambda self, portfolio: True,
)
def test_portfolio_role_summary_view_only_admin_and_domain_requestor(self):
# Test if the user has both 'View-only admin' and 'Domain requestor' roles
@ -1343,8 +1343,8 @@ class TestUser(TestCase):
@patch.multiple(
User,
has_view_all_domains_permission=lambda self, portfolio: True,
has_domain_requests_portfolio_permission=lambda self, portfolio: True,
has_view_all_domains_portfolio_permission=lambda self, portfolio: True,
has_requests_portfolio_permission=lambda self, portfolio: True,
)
def test_portfolio_role_summary_view_only_admin(self):
# Test if the user is recognized as a View-only admin
@ -1353,7 +1353,7 @@ class TestUser(TestCase):
@patch.multiple(
User,
has_base_portfolio_permission=lambda self, portfolio: True,
has_edit_requests=lambda self, portfolio: True,
has_edit_request_portfolio_permission=lambda self, portfolio: True,
has_domains_portfolio_permission=lambda self, portfolio: True,
)
def test_portfolio_role_summary_member_domain_requestor_domain_manager(self):
@ -1361,7 +1361,7 @@ class TestUser(TestCase):
self.assertEqual(self.user.portfolio_role_summary(self.portfolio), ["Domain requestor", "Domain manager"])
@patch.multiple(
User, has_base_portfolio_permission=lambda self, portfolio: True, has_edit_requests=lambda self, portfolio: True
User, has_base_portfolio_permission=lambda self, portfolio: True, has_edit_request_portfolio_permission=lambda self, portfolio: True
)
def test_portfolio_role_summary_member_domain_requestor(self):
# Test if the user has 'Member' and 'Domain requestor' roles
@ -1547,7 +1547,7 @@ class TestUser(TestCase):
portfolio, _ = Portfolio.objects.get_or_create(creator=self.user, organization_name="Hotel California")
user_can_view_all_domains = self.user.has_domains_portfolio_permission(portfolio)
user_can_view_all_requests = self.user.has_domain_requests_portfolio_permission(portfolio)
user_can_view_all_requests = self.user.has_requests_portfolio_permission(portfolio)
self.assertFalse(user_can_view_all_domains)
self.assertFalse(user_can_view_all_requests)
@ -1559,7 +1559,7 @@ class TestUser(TestCase):
)
user_can_view_all_domains = self.user.has_domains_portfolio_permission(portfolio)
user_can_view_all_requests = self.user.has_domain_requests_portfolio_permission(portfolio)
user_can_view_all_requests = self.user.has_requests_portfolio_permission(portfolio)
self.assertTrue(user_can_view_all_domains)
self.assertFalse(user_can_view_all_requests)
@ -1569,7 +1569,7 @@ class TestUser(TestCase):
portfolio_permission.refresh_from_db()
user_can_view_all_domains = self.user.has_domains_portfolio_permission(portfolio)
user_can_view_all_requests = self.user.has_domain_requests_portfolio_permission(portfolio)
user_can_view_all_requests = self.user.has_requests_portfolio_permission(portfolio)
self.assertTrue(user_can_view_all_domains)
self.assertTrue(user_can_view_all_requests)
@ -1577,7 +1577,7 @@ class TestUser(TestCase):
UserDomainRole.objects.get_or_create(user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER)
user_can_view_all_domains = self.user.has_domains_portfolio_permission(portfolio)
user_can_view_all_requests = self.user.has_domain_requests_portfolio_permission(portfolio)
user_can_view_all_requests = self.user.has_requests_portfolio_permission(portfolio)
self.assertTrue(user_can_view_all_domains)
self.assertTrue(user_can_view_all_requests)

View file

@ -12,7 +12,7 @@ def get_domain_requests_json(request):
"""Given the current request,
get all domain requests that are associated with the request user and exclude the APPROVED ones"""
domain_request_ids = get_domain_requests_ids_from_request(request)
domain_request_ids = get_domain_request_ids_from_request(request)
objects = DomainRequest.objects.filter(id__in=domain_request_ids)
unfiltered_total = objects.count()
@ -24,7 +24,7 @@ def get_domain_requests_json(request):
page_number = request.GET.get("page", 1)
page_obj = paginator.get_page(page_number)
domain_requests = [serialize_domain_request(domain_request) for domain_request in page_obj.object_list]
domain_requests = [serialize_domain_request(domain_request, request.user) for domain_request in page_obj.object_list]
return JsonResponse(
{
@ -39,22 +39,20 @@ def get_domain_requests_json(request):
)
def get_domain_requests_ids_from_request(request):
def get_domain_request_ids_from_request(request):
"""Get domain request ids from request.
If portfolio specified, return domain request ids associated with portfolio.
Otherwise, return domain request ids associated with request.user.
"""
portfolio = request.GET.get("portfolio")
filter_condition = Q(creator=request.user)
if portfolio:
domain_requests = DomainRequest.objects.filter(portfolio=portfolio).exclude(
status=DomainRequest.DomainRequestStatus.APPROVED
)
else:
domain_requests = DomainRequest.objects.filter(creator=request.user).exclude(
status=DomainRequest.DomainRequestStatus.APPROVED
)
if request.user.is_org_user(request) and request.user.has_view_all_requests_portfolio_permission(portfolio):
filter_condition = Q(portfolio=portfolio)
else:
filter_condition = Q(portfolio=portfolio, creator=request.user)
domain_requests = DomainRequest.objects.filter(filter_condition).exclude(status=DomainRequest.DomainRequestStatus.APPROVED)
return domain_requests.values_list("id", flat=True)
@ -86,41 +84,38 @@ def apply_sorting(queryset, request):
return queryset.order_by(sort_by)
def serialize_domain_request(domain_request):
def serialize_domain_request(domain_request, user):
# Determine if the request is deletable
is_deletable = domain_request.status in [
DomainRequest.DomainRequestStatus.STARTED,
DomainRequest.DomainRequestStatus.WITHDRAWN,
]
action_url = (
reverse("edit-domain-request", kwargs={"id": domain_request.id})
if domain_request.status
in [
DomainRequest.DomainRequestStatus.STARTED,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
DomainRequest.DomainRequestStatus.WITHDRAWN,
]
else reverse("domain-request-status", kwargs={"pk": domain_request.id})
)
action_label = (
"Edit"
if domain_request.status
in [
DomainRequest.DomainRequestStatus.STARTED,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
DomainRequest.DomainRequestStatus.WITHDRAWN,
]
else "Manage"
)
svg_icon = (
"edit"
if domain_request.status
in [
DomainRequest.DomainRequestStatus.STARTED,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
DomainRequest.DomainRequestStatus.WITHDRAWN,
]
else "settings"
)
# Determine action label based on user permissions and request status
editable_statuses = [
DomainRequest.DomainRequestStatus.STARTED,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
DomainRequest.DomainRequestStatus.WITHDRAWN,
]
if user.has_edit_request_portfolio_permission and domain_request.creator == user:
action_label = "Edit" if domain_request.status in editable_statuses else "Manage"
else:
action_label = "View"
# Map the action label to corresponding URLs and icons
action_url_map = {
"Edit": reverse("edit-domain-request", kwargs={"id": domain_request.id}),
"Manage": reverse("domain-request-status", kwargs={"pk": domain_request.id}),
"View": "#"
}
svg_icon_map = {
"Edit": "edit",
"Manage": "settings",
"View": "visibility"
}
return {
"requested_domain": domain_request.requested_domain.name if domain_request.requested_domain else None,
"last_submitted_date": domain_request.last_submitted_date,
@ -128,7 +123,7 @@ def serialize_domain_request(domain_request):
"created_at": format(domain_request.created_at, "c"), # Serialize to ISO 8601
"id": domain_request.id,
"is_deletable": is_deletable,
"action_url": action_url,
"action_url": action_url_map.get(action_label),
"action_label": action_label,
"svg_icon": svg_icon,
}
"svg_icon": svg_icon_map.get(action_label),
}

View file

@ -450,7 +450,7 @@ class PortfolioDomainRequestsPermission(PortfolioBasePermission):
up from the portfolio's primary key in self.kwargs["pk"]"""
portfolio = self.request.session.get("portfolio")
if not self.request.user.has_domain_requests_portfolio_permission(portfolio):
if not self.request.user.has_requests_portfolio_permission(portfolio):
return False
return super().has_permission()