This commit is contained in:
David Kennedy 2024-10-03 15:56:29 -04:00
parent 9aa3504426
commit 2ecbab459b
No known key found for this signature in database
GPG key ID: 6528A5386E66B96B
8 changed files with 91 additions and 4 deletions

View file

@ -81,6 +81,11 @@ urlpatterns = [
views.PortfolioMembersView.as_view(),
name="members",
),
path(
"member/<int:pk>",
views.PortfolioMemberView.as_view(),
name="member",
),
# path(
# "no-organization-members/",
# views.PortfolioNoMembersView.as_view(),

View file

@ -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:

View file

@ -0,0 +1,32 @@
{% extends 'portfolio_base.html' %}
{% load static field_helpers%}
{% block title %}Organization member {% endblock %}
{% load static %}
{% block portfolio_content %}
<div class="grid-row grid-gap">
<div class="tablet:grid-col-3">
<p class="font-body-md margin-top-0 margin-bottom-2
text-primary-darker text-semibold"
>
<span class="usa-sr-only"> Portfolio name:</span> {{ portfolio }}
</p>
</div>
<div class="tablet:grid-col-9" id="main-content">
{% block messages %}
{% include "includes/form_messages.html" %}
{% endblock %}
<h1>Member</h1>
<p>The name of your organization will be publicly listed as the domain registrant.</p>
</div>
</div>
{% endblock %}

View file

@ -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)

View file

@ -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"),
}

View file

@ -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.

View file

@ -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()

View file

@ -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`.