From 2ecbab459bbdcc7448ae424059ebb09756baf1c9 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Thu, 3 Oct 2024 15:56:29 -0400 Subject: [PATCH] wip --- src/registrar/config/urls.py | 5 +++ .../fixtures_user_portfolio_permissions.py | 3 +- src/registrar/templates/portfolio_member.html | 32 +++++++++++++++++++ .../tests/test_views_members_json.py | 5 +++ src/registrar/views/portfolio_members_json.py | 3 +- src/registrar/views/portfolios.py | 13 ++++++++ src/registrar/views/utility/mixins.py | 23 ++++++++++++- .../views/utility/permission_views.py | 11 ++++++- 8 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 src/registrar/templates/portfolio_member.html diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index 3099f468f..754edca1c 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -81,6 +81,11 @@ urlpatterns = [ views.PortfolioMembersView.as_view(), name="members", ), + path( + "member/", + views.PortfolioMemberView.as_view(), + name="member", + ), # path( # "no-organization-members/", # views.PortfolioNoMembersView.as_view(), diff --git a/src/registrar/fixtures/fixtures_user_portfolio_permissions.py b/src/registrar/fixtures/fixtures_user_portfolio_permissions.py index 3c64eb6b5..0ff740573 100644 --- a/src/registrar/fixtures/fixtures_user_portfolio_permissions.py +++ b/src/registrar/fixtures/fixtures_user_portfolio_permissions.py @@ -7,7 +7,7 @@ from registrar.fixtures.fixtures_users import UserFixture from registrar.models import User from registrar.models.portfolio import Portfolio from registrar.models.user_portfolio_permission import UserPortfolioPermission -from registrar.models.utility.portfolio_helper import UserPortfolioRoleChoices +from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices fake = Faker() logger = logging.getLogger(__name__) @@ -58,6 +58,7 @@ class UserPortfolioPermissionFixture: user=user, portfolio=portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN], + additional_permissions=[UserPortfolioPermissionChoices.EDIT_MEMBERS], ) user_portfolio_permissions_to_create.append(user_portfolio_permission) else: diff --git a/src/registrar/templates/portfolio_member.html b/src/registrar/templates/portfolio_member.html new file mode 100644 index 000000000..55435d3b1 --- /dev/null +++ b/src/registrar/templates/portfolio_member.html @@ -0,0 +1,32 @@ +{% extends 'portfolio_base.html' %} +{% load static field_helpers%} + +{% block title %}Organization member {% endblock %} + +{% load static %} + +{% block portfolio_content %} +
+
+

+ Portfolio name: {{ portfolio }} +

+
+ +
+ + {% block messages %} + {% include "includes/form_messages.html" %} + {% endblock %} + +

Member

+ +

The name of your organization will be publicly listed as the domain registrant.

+ + + +
+
+{% endblock %} diff --git a/src/registrar/tests/test_views_members_json.py b/src/registrar/tests/test_views_members_json.py index 75c3a3a66..1a8238502 100644 --- a/src/registrar/tests/test_views_members_json.py +++ b/src/registrar/tests/test_views_members_json.py @@ -68,6 +68,11 @@ class GetPortfolioMembersJsonTest(TestWithUser, WebTest): roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN], ) + def tearDownClass(cls): + UserPortfolioPermission.objects.all().delete() + Portfolio.objects.all().delete() + User.objects.all().delete() + def setUp(self): super().setUp() self.app.set_user(self.user.username) diff --git a/src/registrar/views/portfolio_members_json.py b/src/registrar/views/portfolio_members_json.py index 133e6750e..34cc08ee7 100644 --- a/src/registrar/views/portfolio_members_json.py +++ b/src/registrar/views/portfolio_members_json.py @@ -2,6 +2,7 @@ from django.http import JsonResponse from django.core.paginator import Paginator from django.contrib.auth.decorators import login_required from django.db.models import Q +from django.urls import reverse from registrar.models.portfolio_invitation import PortfolioInvitation from registrar.models.user import User @@ -118,7 +119,7 @@ def serialize_members(request, portfolio, member, user, admin_ids, portfolio_inv "email": member.email, "is_admin": is_admin, "last_active": last_active, - "action_url": "#", # reverse("members", kwargs={"pk": member.id}), # TODO: Future ticket? + "action_url": reverse("member", kwargs={"pk": member.id}), # TODO: Future ticket? "action_label": ("View" if view_only else "Manage"), "svg_icon": ("visibility" if view_only else "settings"), } diff --git a/src/registrar/views/portfolios.py b/src/registrar/views/portfolios.py index 552fdb6ff..fe15ccd27 100644 --- a/src/registrar/views/portfolios.py +++ b/src/registrar/views/portfolios.py @@ -12,6 +12,7 @@ from registrar.views.utility.permission_views import ( PortfolioDomainsPermissionView, PortfolioBasePermissionView, NoPortfolioDomainsPermissionView, + PortfolioMemberPermissionView, PortfolioMembersPermissionView, ) from django.views.generic import View @@ -51,6 +52,18 @@ class PortfolioMembersView(PortfolioMembersPermissionView, View): return render(request, "portfolio_members.html") +class PortfolioMemberView(PortfolioMemberPermissionView, View): + + template_name = "portfolio_member.html" + model = User + + # def get(self, request): + # """Add additional context data to the template.""" + # return render(request, self.template_name, context=self.get_context_data()) + + + + class PortfolioNoDomainsView(NoPortfolioDomainsPermissionView, View): """Some users have access to the underlying portfolio, but not any domains. This is a custom view which explains that to the user - and denotes who to contact. diff --git a/src/registrar/views/utility/mixins.py b/src/registrar/views/utility/mixins.py index 2cb2a599b..3e3453bed 100644 --- a/src/registrar/views/utility/mixins.py +++ b/src/registrar/views/utility/mixins.py @@ -490,7 +490,28 @@ class PortfolioMembersPermission(PortfolioBasePermission): 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_permission(portfolio): + if not self.request.user.has_view_members_portfolio_permission( + portfolio + ) and not self.request.user.has_edit_members_portfolio_permission(portfolio): + return False + + return super().has_permission() + + +class PortfolioMemberPermission(PortfolioBasePermission): + """Permission mixin that allows access to portfolio member 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_permission( + portfolio + ) and not self.request.user.has_edit_members_portfolio_permission(portfolio): return False return super().has_permission() diff --git a/src/registrar/views/utility/permission_views.py b/src/registrar/views/utility/permission_views.py index 414e58275..c9ffa8625 100644 --- a/src/registrar/views/utility/permission_views.py +++ b/src/registrar/views/utility/permission_views.py @@ -19,6 +19,7 @@ from .mixins import ( UserProfilePermission, PortfolioBasePermission, PortfolioMembersPermission, + PortfolioMemberPermission, DomainRequestPortfolioViewonlyPermission, ) import logging @@ -253,7 +254,15 @@ class PortfolioDomainRequestsPermissionView(PortfolioDomainRequestsPermission, P class PortfolioMembersPermissionView(PortfolioMembersPermission, PortfolioBasePermissionView, abc.ABC): - """Abstract base view for portfolio domain request views that enforces permissions. + """Abstract base view for portfolio members views that enforces permissions. + + This abstract view cannot be instantiated. Actual views must specify + `template_name`. + """ + + +class PortfolioMemberPermissionView(PortfolioMemberPermission, PortfolioBasePermissionView, abc.ABC): + """Abstract base view for portfolio member views that enforces permissions. This abstract view cannot be instantiated. Actual views must specify `template_name`.