working permissions

This commit is contained in:
Rachid Mrad 2024-07-17 13:57:04 -04:00
parent ebb95a7d67
commit 777be646a4
No known key found for this signature in database
10 changed files with 69 additions and 120 deletions

View file

@ -654,7 +654,6 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
"user_permissions", "user_permissions",
"portfolio", "portfolio",
"portfolio_roles", "portfolio_roles",
"portfolio_permissions",
) )
}, },
), ),
@ -685,7 +684,6 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
"groups", "groups",
"portfolio", "portfolio",
"portfolio_roles", "portfolio_roles",
"portfolio_permissions",
) )
}, },
), ),
@ -717,7 +715,6 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
"date_joined", "date_joined",
"portfolio", "portfolio",
"portfolio_roles", "portfolio_roles",
"portfolio_permissions",
] ]
list_filter = ( list_filter = (

View file

@ -240,6 +240,7 @@ TEMPLATES = [
"registrar.context_processors.canonical_path", "registrar.context_processors.canonical_path",
"registrar.context_processors.is_demo_site", "registrar.context_processors.is_demo_site",
"registrar.context_processors.is_production", "registrar.context_processors.is_production",
"registrar.context_processors.portfolio_permissions",
], ],
}, },
}, },

View file

@ -40,18 +40,10 @@ def is_production(request):
return {"IS_PRODUCTION": settings.IS_PRODUCTION} return {"IS_PRODUCTION": settings.IS_PRODUCTION}
def has_base_portfolio_permission(request): def portfolio_permissions(request):
"""""" """"""
return {"has_base_portfolio_permission": request.user.has_portfolio_permissions(User.UserPortfolioPermissionChoices.VIEW_PORTFOLIO)} return {
"has_base_portfolio_permission": request.user.has_portfolio_permission(User.UserPortfolioPermissionChoices.VIEW_PORTFOLIO),
def has_domains_portfolio_permission(request): "has_domains_portfolio_permission": request.user.has_portfolio_permission(User.UserPortfolioPermissionChoices.VIEW_DOMAINS),
"""""" "has_domain_requests_portfolio_permission": request.user.has_portfolio_permission(User.UserPortfolioPermissionChoices.VIEW_REQUESTS)
return {"has_domains_portfolio_permission": request.user.has_portfolio_permissions(User.UserPortfolioPermissionChoices.VIEW_DOMAINS)} }
def has_requests_portfolio_permission(request):
""""""
return {"has_requests_portfolio_permission": request.user.has_portfolio_permissions(User.UserPortfolioPermissionChoices.VIEW_REQUESTS)}
def has_organization_portfolio_permission(request):
""""""
return {"has_organization_portfolio_permission": request.user.has_portfolio_permissions(User.UserPortfolioPermissionChoices.VIEW_PORTFOLIO)}

View file

@ -1,4 +1,4 @@
# Generated by Django 4.2.10 on 2024-07-15 22:07 # Generated by Django 4.2.10 on 2024-07-17 17:12
from django.conf import settings from django.conf import settings
import django.contrib.postgres.fields import django.contrib.postgres.fields
@ -24,29 +24,6 @@ class Migration(migrations.Migration):
to="registrar.portfolio", to="registrar.portfolio",
), ),
), ),
migrations.AddField(
model_name="user",
name="portfolio_permissions",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(
choices=[
("view_domains", "View all domains and domain reports"),
("edit_domains", "User is a manager on a domain"),
("view_member", "View members"),
("edit_member", "Create and edit members"),
("view_requests", "View requests"),
("edit_requests", "Create and edit requests"),
("view_portfolio", "View organization"),
("edit_portfolio", "Edit organization"),
],
max_length=50,
),
blank=True,
help_text="Select one or more permissions.",
null=True,
size=None,
),
),
migrations.AddField( migrations.AddField(
model_name="user", model_name="user",
name="portfolio_roles", name="portfolio_roles",

View file

@ -63,6 +63,7 @@ class User(AbstractUser):
class UserPortfolioRoleChoices(models.TextChoices): class UserPortfolioRoleChoices(models.TextChoices):
""" """
Roles make it easier for admins to look at
""" """
ORGANIZATION_ADMIN = "organization_admin", "Admin" ORGANIZATION_ADMIN = "organization_admin", "Admin"
@ -149,15 +150,15 @@ class User(AbstractUser):
help_text="Select one or more roles.", help_text="Select one or more roles.",
) )
portfolio_permissions = ArrayField( # portfolio_permissions = ArrayField(
models.CharField( # models.CharField(
max_length=50, # max_length=50,
choices=UserPortfolioPermissionChoices.choices, # choices=UserPortfolioPermissionChoices.choices,
), # ),
null=True, # null=True,
blank=True, # blank=True,
help_text="Select one or more permissions.", # help_text="Select one or more permissions.",
) # )
phone = PhoneNumberField( phone = PhoneNumberField(
null=True, null=True,
@ -252,30 +253,30 @@ class User(AbstractUser):
"""Do not rely on roles when testing for perms in views""" """Do not rely on roles when testing for perms in views"""
return role in self.portfolio_roles if self.portfolio_roles else False return role in self.portfolio_roles if self.portfolio_roles else False
def has_portfolio_permissions(self, portfolio_permission): def has_portfolio_permission(self, portfolio_permission):
"""The views should only call this guy when testing for perms and not rely on roles""" """The views should only call this guy when testing for perms and not rely on roles"""
# TODO: this does not seem to be working # TODO: this does not seem to be working
if portfolio_permission == self.UserPortfolioPermissionChoices.EDIT_DOMAINS and self.domains.exists(): # if portfolio_permission == self.UserPortfolioPermissionChoices.EDIT_DOMAINS and self.domains.exists():
print(f'portfolio_permission {portfolio_permission}') # return True
return True
if not self.portfolio:
return False
print(f'portfolio_permission {portfolio_permission}') portfolio_permissions = self.get_portfolio_permissions()
return portfolio_permission in self.portfolio_permissions if self.portfolio_permissions else False return portfolio_permission in portfolio_permissions
def save(self, *args, **kwargs): def get_portfolio_permissions(self):
self.update_permissions_from_roles() """
super().save(*args, **kwargs) Retrieve the permissions for the user's portfolio roles.
"""
portfolio_permissions = set() # Use a set to avoid duplicate permissions
def update_permissions_from_roles(self): for role in self.portfolio_roles:
print('update permissions when saving') if role in self.PORTFOLIO_ROLE_PERMISSIONS:
new_portfolio_permissions = set(self.portfolio_permissions or []) portfolio_permissions.update(self.PORTFOLIO_ROLE_PERMISSIONS[role])
print(f'new_portfolio_permissions {new_portfolio_permissions}') return list(portfolio_permissions) # Convert back to list if necessary
for role in self.portfolio_roles or []:
print(f'role {role}')
new_portfolio_permissions.update(self.PORTFOLIO_ROLE_PERMISSIONS.get(role, []))
self.portfolio_permissions = list(new_portfolio_permissions)
@classmethod @classmethod
def needs_identity_verification(cls, email, uuid): def needs_identity_verification(cls, email, uuid):

View file

@ -6,7 +6,7 @@ import logging
from urllib.parse import parse_qs from urllib.parse import parse_qs
from django.urls import reverse from django.urls import reverse
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from registrar.context_processors import has_base_portfolio_permission, has_domains_portfolio_permission, has_requests_portfolio_permission from registrar.context_processors import portfolio_permissions
from registrar.models.user import User from registrar.models.user import User
from waffle.decorators import flag_is_active from waffle.decorators import flag_is_active
@ -148,19 +148,21 @@ class CheckPortfolioMiddleware:
if request.user.is_authenticated: if request.user.is_authenticated:
# user_portfolios = Portfolio.objects.filter(creator=request.user) # user_portfolios = Portfolio.objects.filter(creator=request.user)
if has_base_portfolio_permission(request): permission_dict = portfolio_permissions(request)
# print('user has portfolio') has_portfolio_base_permission = permission_dict['has_base_portfolio_permission']
if has_portfolio_base_permission:
portfolio = request.user.portfolio portfolio = request.user.portfolio
if has_domains_portfolio_permission(request): permission_dict = portfolio_permissions(request)
has_portfolio_domains_permission = permission_dict['has_domains_portfolio_permission']
if has_portfolio_domains_permission:
portfolio_redirect = reverse("portfolio-domains", kwargs={"portfolio_id": portfolio.id}) portfolio_redirect = reverse("portfolio-domains", kwargs={"portfolio_id": portfolio.id})
elif has_requests_portfolio_permission(request):
portfolio_redirect = reverse("portfolio-requests", kwargs={"portfolio_id": portfolio.id})
else: else:
# View organization is the lowest access # View organization is the lowest access
portfolio_redirect = reverse("portfolio-organization", kwargs={"portfolio_id": portfolio.id}) portfolio_redirect = reverse("portfolio-organization", kwargs={"portfolio_id": portfolio.id})
return HttpResponseRedirect(portfolio_redirect) return HttpResponseRedirect(portfolio_redirect)
# print('user does not have a portfolio')
return None return None

View file

@ -12,7 +12,15 @@
</a> </a>
</li> </li>
{% endif %} {% endif %}
{% if has_requests_portfolio_permission %}
<li class="usa-sidenav__item">
{% url 'portfolio-organization' portfolio.id as url %}
<a href="{{ url }}" {% if request.path == url %}class="usa-current"{% endif %}>
Organization
</a>
</li>
{% if has_domain_requests_portfolio_permission %}
<li class="usa-sidenav__item"> <li class="usa-sidenav__item">
{% url 'portfolio-domain-requests' portfolio.id as url %} {% url 'portfolio-domain-requests' portfolio.id as url %}
<a href="{{ url }}" {% if request.path == url %}class="usa-current"{% endif %}> <a href="{{ url }}" {% if request.path == url %}class="usa-current"{% endif %}>
@ -26,12 +34,7 @@
Members Members
</a> </a>
</li> </li>
<li class="usa-sidenav__item">
{% url 'portfolio-organization' portfolio.id as url %}
<a href="{{ url }}" {% if request.path == url %}class="usa-current"{% endif %}>
Organization
</a>
</li>
<li class="usa-sidenav__item"> <li class="usa-sidenav__item">
<a href="#"> <a href="#">
Senior official Senior official

View file

@ -1,6 +1,6 @@
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from registrar.models.portfolio import Portfolio from registrar.models.portfolio import Portfolio
from registrar.views.utility.permission_views import PortfolioDomainRequestsPermissionView, PortfolioDomainsPermissionView, PortfolioOrganizationssPermissionView from registrar.views.utility.permission_views import PortfolioDomainRequestsPermissionView, PortfolioDomainsPermissionView, PortfolioBasePermissionView
from waffle.decorators import flag_is_active from waffle.decorators import flag_is_active
from django.views.generic import View from django.views.generic import View
@ -35,7 +35,7 @@ class PortfolioDomainRequestsView(PortfolioDomainRequestsPermissionView, View):
return render(request, "portfolio_requests.html", context) return render(request, "portfolio_requests.html", context)
class PortfolioOrganizationView(PortfolioOrganizationssPermissionView, View): class PortfolioOrganizationView(PortfolioBasePermissionView, View):
template_name = "portfolio_organization.html" template_name = "portfolio_organization.html"

View file

@ -2,7 +2,7 @@
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from registrar.context_processors import has_base_portfolio_permission, has_domains_portfolio_permission, has_organization_portfolio_permission, has_requests_portfolio_permission from registrar.context_processors import portfolio_permissions
from registrar.models import ( from registrar.models import (
Domain, Domain,
DomainRequest, DomainRequest,
@ -402,7 +402,7 @@ class UserProfilePermission(PermissionsLoginMixin):
return True return True
class PortfolioPermission(PermissionsLoginMixin): class PortfolioBasePermission(PermissionsLoginMixin):
"""Permission mixin that redirects to portfolio pages if user """Permission mixin that redirects to portfolio pages if user
has access, otherwise 403""" has access, otherwise 403"""
@ -415,15 +415,15 @@ class PortfolioPermission(PermissionsLoginMixin):
if not self.request.user.is_authenticated: if not self.request.user.is_authenticated:
return False return False
# portfolio_id = self.kwargs["pk"] permission_dict = portfolio_permissions(self.request)
# portfolio = Portfolio.objects.get(pk=portfolio_id) has_permission = permission_dict['has_base_portfolio_permission']
if not has_base_portfolio_permission(self.request): if not has_permission:
return False return False
return True return True
class PortfolioDomainsPermission(PortfolioPermission): class PortfolioDomainsPermission(PortfolioBasePermission):
""" """
""" """
@ -431,18 +431,16 @@ class PortfolioDomainsPermission(PortfolioPermission):
""" """
""" """
permission_dict = has_domains_portfolio_permission(self.request) permission_dict = portfolio_permissions(self.request)
has_permission = permission_dict['has_domains_portfolio_permission'] has_permission = permission_dict['has_domains_portfolio_permission']
if not has_permission: if not has_permission:
return False return False
print('return true')
return True return True
class PortfolioDomainRequestsPermission(PortfolioPermission): class PortfolioDomainRequestsPermission(PortfolioBasePermission):
""" """
""" """
@ -450,24 +448,8 @@ class PortfolioDomainRequestsPermission(PortfolioPermission):
""" """
""" """
permission_dict = has_requests_portfolio_permission(self.request) permission_dict = portfolio_permissions(self.request)
has_permission = permission_dict['has_requests_portfolio_permission'] has_permission = permission_dict['has_domain_requests_portfolio_permission']
if not has_permission:
return False
return True
class PortfolioOrganizationssPermission(PortfolioPermission):
"""
"""
def has_permission(self):
"""
"""
permission_dict = has_organization_portfolio_permission(self.request)
has_permission = permission_dict['has_organization_portfolio_permission']
if not has_permission: if not has_permission:
return False return False

View file

@ -15,10 +15,9 @@ from .mixins import (
DomainRequestWizardPermission, DomainRequestWizardPermission,
PortfolioDomainRequestsPermission, PortfolioDomainRequestsPermission,
PortfolioDomainsPermission, PortfolioDomainsPermission,
PortfolioOrganizationssPermission,
UserDeleteDomainRolePermission, UserDeleteDomainRolePermission,
UserProfilePermission, UserProfilePermission,
PortfolioPermission, PortfolioBasePermission,
) )
import logging import logging
@ -169,7 +168,7 @@ class UserProfilePermissionView(UserProfilePermission, DetailView, abc.ABC):
raise NotImplementedError raise NotImplementedError
class PortfolioBasePermissionView(PortfolioPermission, DetailView, abc.ABC): class PortfolioBasePermissionView(PortfolioBasePermission, DetailView, abc.ABC):
"""Abstract base view for portfolio views that enforces permissions. """Abstract base view for portfolio views that enforces permissions.
This abstract view cannot be instantiated. Actual views must specify This abstract view cannot be instantiated. Actual views must specify
@ -196,8 +195,3 @@ class PortfolioDomainsPermissionView(PortfolioDomainsPermission, PortfolioBasePe
class PortfolioDomainRequestsPermissionView(PortfolioDomainRequestsPermission, PortfolioBasePermissionView, abc.ABC): class PortfolioDomainRequestsPermissionView(PortfolioDomainRequestsPermission, PortfolioBasePermissionView, abc.ABC):
""" """
""" """
class PortfolioOrganizationssPermissionView(PortfolioOrganizationssPermission, PortfolioBasePermissionView, abc.ABC):
"""
"""