mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-20 03:19:24 +02:00
Merge branch 'main' into rjm/2351-org-requests-page
This commit is contained in:
commit
847bcbe9b7
11 changed files with 185 additions and 13 deletions
|
@ -67,6 +67,8 @@ def portfolio_permissions(request):
|
|||
"has_edit_request_portfolio_permission": False,
|
||||
"has_view_suborganization_portfolio_permission": False,
|
||||
"has_edit_suborganization_portfolio_permission": False,
|
||||
"has_view_members_portfolio_permission": False,
|
||||
"has_edit_members_portfolio_permission": False,
|
||||
"portfolio": None,
|
||||
"has_organization_feature_flag": False,
|
||||
}
|
||||
|
@ -86,6 +88,8 @@ def portfolio_permissions(request):
|
|||
"has_any_requests_portfolio_permission": request.user.has_any_requests_portfolio_permission(
|
||||
portfolio
|
||||
),
|
||||
"has_view_members_portfolio_permission": request.user.has_view_members_portfolio_permission(portfolio),
|
||||
"has_edit_members_portfolio_permission": request.user.has_edit_members_portfolio_permission(portfolio),
|
||||
"portfolio": portfolio,
|
||||
"has_organization_feature_flag": True,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 4.2.10 on 2024-09-05 23:39
|
||||
# Generated by Django 4.2.10 on 2024-09-04 21:29
|
||||
|
||||
import django.contrib.postgres.fields
|
||||
from django.db import migrations, models
|
||||
|
@ -19,9 +19,10 @@ class Migration(migrations.Migration):
|
|||
choices=[
|
||||
("view_all_domains", "View all domains and domain reports"),
|
||||
("view_managed_domains", "View managed domains"),
|
||||
("view_member", "View members"),
|
||||
("edit_member", "Create and edit members"),
|
||||
("view_members", "View members"),
|
||||
("edit_members", "Create and edit members"),
|
||||
("view_all_requests", "View all requests"),
|
||||
("view_created_requests", "View created requests"),
|
||||
("edit_requests", "Create and edit requests"),
|
||||
("view_portfolio", "View organization"),
|
||||
("edit_portfolio", "Edit organization"),
|
||||
|
@ -44,9 +45,10 @@ class Migration(migrations.Migration):
|
|||
choices=[
|
||||
("view_all_domains", "View all domains and domain reports"),
|
||||
("view_managed_domains", "View managed domains"),
|
||||
("view_member", "View members"),
|
||||
("edit_member", "Create and edit members"),
|
||||
("view_members", "View members"),
|
||||
("edit_members", "Create and edit members"),
|
||||
("view_all_requests", "View all requests"),
|
||||
("view_created_requests", "View created requests"),
|
||||
("edit_requests", "Create and edit requests"),
|
||||
("view_portfolio", "View organization"),
|
||||
("edit_portfolio", "Edit organization"),
|
||||
|
|
|
@ -197,6 +197,41 @@ 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):
|
||||
# BEGIN
|
||||
# Note code below is to add organization_request feature
|
||||
request = HttpRequest()
|
||||
request.user = self
|
||||
has_organization_requests_flag = flag_is_active(request, "organization_requests")
|
||||
if not has_organization_requests_flag:
|
||||
return False
|
||||
# END
|
||||
return self._has_portfolio_permission(
|
||||
portfolio, UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS
|
||||
) or self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.VIEW_CREATED_REQUESTS)
|
||||
|
||||
def has_view_members_portfolio_permission(self, portfolio):
|
||||
# BEGIN
|
||||
# Note code below is to add organization_request feature
|
||||
request = HttpRequest()
|
||||
request.user = self
|
||||
has_organization_members_flag = flag_is_active(request, "organization_members")
|
||||
if not has_organization_members_flag:
|
||||
return False
|
||||
# END
|
||||
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.VIEW_MEMBERS)
|
||||
|
||||
def has_edit_members_portfolio_permission(self, portfolio):
|
||||
# BEGIN
|
||||
# Note code below is to add organization_request feature
|
||||
request = HttpRequest()
|
||||
request.user = self
|
||||
has_organization_members_flag = flag_is_active(request, "organization_members")
|
||||
if not has_organization_members_flag:
|
||||
return False
|
||||
# END
|
||||
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.EDIT_MEMBERS)
|
||||
|
||||
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)
|
||||
|
|
|
@ -16,8 +16,8 @@ class UserPortfolioPermission(TimeStampedModel):
|
|||
PORTFOLIO_ROLE_PERMISSIONS = {
|
||||
UserPortfolioRoleChoices.ORGANIZATION_ADMIN: [
|
||||
UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS,
|
||||
UserPortfolioPermissionChoices.VIEW_MEMBER,
|
||||
UserPortfolioPermissionChoices.EDIT_MEMBER,
|
||||
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
||||
UserPortfolioPermissionChoices.EDIT_MEMBERS,
|
||||
UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS,
|
||||
UserPortfolioPermissionChoices.EDIT_REQUESTS,
|
||||
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
|
||||
|
@ -28,7 +28,7 @@ class UserPortfolioPermission(TimeStampedModel):
|
|||
],
|
||||
UserPortfolioRoleChoices.ORGANIZATION_ADMIN_READ_ONLY: [
|
||||
UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS,
|
||||
UserPortfolioPermissionChoices.VIEW_MEMBER,
|
||||
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
||||
UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS,
|
||||
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
|
||||
# Domain: field specific permissions
|
||||
|
|
|
@ -17,8 +17,8 @@ class UserPortfolioPermissionChoices(models.TextChoices):
|
|||
VIEW_ALL_DOMAINS = "view_all_domains", "View all domains and domain reports"
|
||||
VIEW_MANAGED_DOMAINS = "view_managed_domains", "View managed domains"
|
||||
|
||||
VIEW_MEMBER = "view_member", "View members"
|
||||
EDIT_MEMBER = "edit_member", "Create and edit members"
|
||||
VIEW_MEMBERS = "view_members", "View members"
|
||||
EDIT_MEMBERS = "edit_members", "Create and edit members"
|
||||
|
||||
VIEW_ALL_REQUESTS = "view_all_requests", "View all requests"
|
||||
EDIT_REQUESTS = "edit_requests", "Create and edit requests"
|
||||
|
|
|
@ -46,11 +46,11 @@
|
|||
Domains
|
||||
</a>
|
||||
</li>
|
||||
<li class="usa-nav__primary-item">
|
||||
<!-- <li class="usa-nav__primary-item">
|
||||
<a href="#" class="usa-nav-link">
|
||||
Domain groups
|
||||
</a>
|
||||
</li>
|
||||
</li> -->
|
||||
|
||||
<li class="usa-nav__primary-item">
|
||||
<!-- user hasone of the view permissions plus the edit permission, show the dropdown -->
|
||||
|
@ -92,11 +92,13 @@
|
|||
</li>
|
||||
|
||||
|
||||
{% if has_view_members_portfolio_permission %}
|
||||
<li class="usa-nav__primary-item">
|
||||
<a href="#" class="usa-nav-link">
|
||||
Members
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="usa-nav__primary-item">
|
||||
{% url 'organization' as url %}
|
||||
<!-- Move the padding from the a to the span so that the descenders do not get cut off -->
|
||||
|
|
|
@ -1603,6 +1603,7 @@ class TestUser(TestCase):
|
|||
self.assertFalse(self.user.has_contact_info())
|
||||
|
||||
@less_console_noise_decorator
|
||||
@override_flag("organization_requests", active=True)
|
||||
def test_has_portfolio_permission(self):
|
||||
"""
|
||||
0. Returns False when user does not have a permission
|
||||
|
@ -1624,7 +1625,10 @@ class TestUser(TestCase):
|
|||
portfolio_permission, _ = UserPortfolioPermission.objects.get_or_create(
|
||||
portfolio=portfolio,
|
||||
user=self.user,
|
||||
additional_permissions=[UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS],
|
||||
additional_permissions=[
|
||||
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
|
||||
UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS,
|
||||
],
|
||||
)
|
||||
|
||||
user_can_view_all_domains = self.user.has_any_domains_portfolio_permission(portfolio)
|
||||
|
|
|
@ -233,6 +233,7 @@ class TestPortfolio(WebTest):
|
|||
self.assertContains(response, 'for="id_city"')
|
||||
|
||||
@less_console_noise_decorator
|
||||
@override_flag("organization_requests", active=True)
|
||||
def test_accessible_pages_when_user_does_not_have_permission(self):
|
||||
"""Tests which pages are accessible when user does not have portfolio permissions"""
|
||||
self.app.set_user(self.user.username)
|
||||
|
@ -283,6 +284,7 @@ class TestPortfolio(WebTest):
|
|||
self.assertEquals(domain_request_page.status_code, 403)
|
||||
|
||||
@less_console_noise_decorator
|
||||
@override_flag("organization_requests", active=True)
|
||||
def test_accessible_pages_when_user_does_not_have_role(self):
|
||||
"""Test that admin / memmber roles are associated with the right access"""
|
||||
self.app.set_user(self.user.username)
|
||||
|
@ -536,6 +538,102 @@ class TestPortfolio(WebTest):
|
|||
self.assertContains(response, "Domain name")
|
||||
permission.delete()
|
||||
|
||||
@less_console_noise_decorator
|
||||
@override_flag("organization_feature", active=True)
|
||||
@override_flag("organization_requests", active=False)
|
||||
def test_organization_requests_waffle_flag_off_hides_nav_link_and_restricts_permission(self):
|
||||
"""Setting the organization_requests waffle off hides the nav link and restricts access to the requests page"""
|
||||
self.app.set_user(self.user.username)
|
||||
|
||||
UserPortfolioPermission.objects.get_or_create(
|
||||
user=self.user,
|
||||
portfolio=self.portfolio,
|
||||
additional_permissions=[
|
||||
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
|
||||
UserPortfolioPermissionChoices.VIEW_CREATED_REQUESTS,
|
||||
UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS,
|
||||
UserPortfolioPermissionChoices.EDIT_REQUESTS,
|
||||
],
|
||||
)
|
||||
|
||||
home = self.app.get(reverse("home")).follow()
|
||||
|
||||
self.assertContains(home, "Hotel California")
|
||||
self.assertNotContains(home, "Domain requests")
|
||||
|
||||
domain_requests = self.app.get(reverse("domain-requests"), expect_errors=True)
|
||||
self.assertEqual(domain_requests.status_code, 403)
|
||||
|
||||
@less_console_noise_decorator
|
||||
@override_flag("organization_feature", active=True)
|
||||
@override_flag("organization_requests", active=True)
|
||||
def test_organization_requests_waffle_flag_on_shows_nav_link_and_allows_permission(self):
|
||||
"""Setting the organization_requests waffle on shows the nav link and allows access to the requests page"""
|
||||
self.app.set_user(self.user.username)
|
||||
|
||||
UserPortfolioPermission.objects.get_or_create(
|
||||
user=self.user,
|
||||
portfolio=self.portfolio,
|
||||
additional_permissions=[
|
||||
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
|
||||
UserPortfolioPermissionChoices.VIEW_CREATED_REQUESTS,
|
||||
UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS,
|
||||
UserPortfolioPermissionChoices.EDIT_REQUESTS,
|
||||
],
|
||||
)
|
||||
|
||||
home = self.app.get(reverse("home")).follow()
|
||||
|
||||
self.assertContains(home, "Hotel California")
|
||||
self.assertContains(home, "Domain requests")
|
||||
|
||||
domain_requests = self.app.get(reverse("domain-requests"))
|
||||
self.assertEqual(domain_requests.status_code, 200)
|
||||
|
||||
@less_console_noise_decorator
|
||||
@override_flag("organization_feature", active=True)
|
||||
@override_flag("organization_members", active=False)
|
||||
def test_organization_members_waffle_flag_off_hides_nav_link(self):
|
||||
"""Setting the organization_members waffle off hides the nav link"""
|
||||
self.app.set_user(self.user.username)
|
||||
|
||||
UserPortfolioPermission.objects.get_or_create(
|
||||
user=self.user,
|
||||
portfolio=self.portfolio,
|
||||
additional_permissions=[
|
||||
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
|
||||
UserPortfolioPermissionChoices.VIEW_CREATED_REQUESTS,
|
||||
UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS,
|
||||
UserPortfolioPermissionChoices.EDIT_REQUESTS,
|
||||
],
|
||||
)
|
||||
|
||||
home = self.app.get(reverse("home")).follow()
|
||||
|
||||
self.assertContains(home, "Hotel California")
|
||||
self.assertNotContains(home, "Members")
|
||||
|
||||
@less_console_noise_decorator
|
||||
@override_flag("organization_feature", active=True)
|
||||
@override_flag("organization_members", active=True)
|
||||
def test_organization_members_waffle_flag_on_shows_nav_link(self):
|
||||
"""Setting the organization_members waffle on shows the nav link"""
|
||||
self.app.set_user(self.user.username)
|
||||
|
||||
UserPortfolioPermission.objects.get_or_create(
|
||||
user=self.user,
|
||||
portfolio=self.portfolio,
|
||||
additional_permissions=[
|
||||
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
|
||||
UserPortfolioPermissionChoices.VIEW_MEMBERS,
|
||||
],
|
||||
)
|
||||
|
||||
home = self.app.get(reverse("home")).follow()
|
||||
|
||||
self.assertContains(home, "Hotel California")
|
||||
self.assertContains(home, "Members")
|
||||
|
||||
@less_console_noise_decorator
|
||||
@override_flag("organization_feature", active=True)
|
||||
def test_portfolio_domain_requests_page_when_user_has_no_permissions(self):
|
||||
|
|
|
@ -7,5 +7,6 @@ from .permission_views import (
|
|||
DomainRequestPermissionWithdrawView,
|
||||
DomainInvitationPermissionDeleteView,
|
||||
DomainRequestWizardPermissionView,
|
||||
PortfolioMembersPermission,
|
||||
)
|
||||
from .api_views import get_senior_official_from_federal_agency_json
|
||||
|
|
|
@ -454,3 +454,20 @@ class PortfolioDomainRequestsPermission(PortfolioBasePermission):
|
|||
return False
|
||||
|
||||
return super().has_permission()
|
||||
|
||||
|
||||
class PortfolioMembersPermission(PortfolioBasePermission):
|
||||
"""Permission mixin that allows access to portfolio members pages if user
|
||||
has access, otherwise 403"""
|
||||
|
||||
def has_permission(self):
|
||||
"""Check if this user has access to members for this portfolio.
|
||||
|
||||
The user is in self.request.user and the portfolio can be looked
|
||||
up from the portfolio's primary key in self.kwargs["pk"]"""
|
||||
|
||||
portfolio = self.request.session.get("portfolio")
|
||||
if not self.request.user.has_view_members(portfolio):
|
||||
return False
|
||||
|
||||
return super().has_permission()
|
||||
|
|
|
@ -18,6 +18,7 @@ from .mixins import (
|
|||
UserDeleteDomainRolePermission,
|
||||
UserProfilePermission,
|
||||
PortfolioBasePermission,
|
||||
PortfolioMembersPermission,
|
||||
)
|
||||
import logging
|
||||
|
||||
|
@ -229,3 +230,11 @@ class PortfolioDomainRequestsPermissionView(PortfolioDomainRequestsPermission, P
|
|||
This abstract view cannot be instantiated. Actual views must specify
|
||||
`template_name`.
|
||||
"""
|
||||
|
||||
|
||||
class PortfolioMembersPermissionView(PortfolioMembersPermission, 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`.
|
||||
"""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue