build our permissions a bit more

This commit is contained in:
Rachid Mrad 2024-07-16 13:12:13 -04:00
parent b1ae220602
commit ebb95a7d67
No known key found for this signature in database
10 changed files with 183 additions and 52 deletions

View file

@ -683,6 +683,9 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
"fields": ( "fields": (
"is_active", "is_active",
"groups", "groups",
"portfolio",
"portfolio_roles",
"portfolio_permissions",
) )
}, },
), ),
@ -712,6 +715,9 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
"Important dates", "Important dates",
"last_login", "last_login",
"date_joined", "date_joined",
"portfolio",
"portfolio_roles",
"portfolio_permissions",
] ]
list_filter = ( list_filter = (

View file

@ -25,7 +25,7 @@ from registrar.views.domain_request import Step
from registrar.views.domain_requests_json import get_domain_requests_json from registrar.views.domain_requests_json import get_domain_requests_json
from registrar.views.domains_json import get_domains_json from registrar.views.domains_json import get_domains_json
from registrar.views.utility import always_404 from registrar.views.utility import always_404
from registrar.views.portfolios import portfolio_domains, portfolio_domain_requests from registrar.views.portfolios import PortfolioDomainsView, PortfolioDomainRequestsView, PortfolioOrganizationView
from api.views import available, get_current_federal, get_current_full from api.views import available, get_current_federal, get_current_full
@ -61,14 +61,19 @@ urlpatterns = [
path("", views.index, name="home"), path("", views.index, name="home"),
path( path(
"portfolio/<int:portfolio_id>/domains/", "portfolio/<int:portfolio_id>/domains/",
portfolio_domains, PortfolioDomainsView.as_view(),
name="portfolio-domains", name="portfolio-domains",
), ),
path( path(
"portfolio/<int:portfolio_id>/domain_requests/", "portfolio/<int:portfolio_id>/domain_requests/",
portfolio_domain_requests, PortfolioDomainRequestsView.as_view(),
name="portfolio-domain-requests", name="portfolio-domain-requests",
), ),
path(
"portfolio/<int:portfolio_id>/organization/",
PortfolioOrganizationView.as_view(),
name="portfolio-organization",
),
path( path(
"admin/logout/", "admin/logout/",
RedirectView.as_view(pattern_name="logout", permanent=False), RedirectView.as_view(pattern_name="logout", permanent=False),

View file

@ -1,5 +1,7 @@
from django.conf import settings from django.conf import settings
from registrar.models.user import User
def language_code(request): def language_code(request):
"""Add LANGUAGE_CODE to the template context. """Add LANGUAGE_CODE to the template context.
@ -36,3 +38,20 @@ def is_demo_site(request):
def is_production(request): def is_production(request):
"""Add a boolean if this is our production site.""" """Add a boolean if this is our production site."""
return {"IS_PRODUCTION": settings.IS_PRODUCTION} return {"IS_PRODUCTION": settings.IS_PRODUCTION}
def has_base_portfolio_permission(request):
""""""
return {"has_base_portfolio_permission": request.user.has_portfolio_permissions(User.UserPortfolioPermissionChoices.VIEW_PORTFOLIO)}
def has_domains_portfolio_permission(request):
""""""
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

@ -255,8 +255,12 @@ class User(AbstractUser):
def has_portfolio_permissions(self, portfolio_permission): def has_portfolio_permissions(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
if portfolio_permission == self.UserPortfolioPermissionChoices.EDIT_DOMAINS and self.domains.exists(): if portfolio_permission == self.UserPortfolioPermissionChoices.EDIT_DOMAINS and self.domains.exists():
return self.domains print(f'portfolio_permission {portfolio_permission}')
return True
print(f'portfolio_permission {portfolio_permission}')
return portfolio_permission in self.portfolio_permissions if self.portfolio_permissions else False return portfolio_permission in self.portfolio_permissions if self.portfolio_permissions else False

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.models.portfolio import Portfolio from registrar.context_processors import has_base_portfolio_permission, has_domains_portfolio_permission, has_requests_portfolio_permission
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,13 +148,19 @@ 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)
required_permission = User.UserPortfolioPermissionChoices.VIEW_PORTFOLIO if has_base_portfolio_permission(request):
# print('user has portfolio')
if request.user.has_portfolio_permissions(required_permission):
print('user has portfolio')
portfolio = request.user.portfolio portfolio = request.user.portfolio
home_with_portfolio = reverse("portfolio-domains", kwargs={"portfolio_id": portfolio.id})
return HttpResponseRedirect(home_with_portfolio) if has_domains_portfolio_permission(request):
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:
# View organization is the lowest access
portfolio_redirect = reverse("portfolio-organization", kwargs={"portfolio_id": portfolio.id})
return HttpResponseRedirect(portfolio_redirect)
print('user does not have a portfolio') # print('user does not have a portfolio')
return None return None

View file

@ -0,0 +1,8 @@
{% extends 'portfolio.html' %}
{% load static %}
{% block portfolio_content %}
<h1>Organization</h1>
{% endblock %}

View file

@ -4,26 +4,31 @@
<nav aria-label=""> <nav aria-label="">
<h2 class="margin-top-0 text-semibold">{{ portfolio.organization_name }}</h2> <h2 class="margin-top-0 text-semibold">{{ portfolio.organization_name }}</h2>
<ul class="usa-sidenav usa-sidenav--portfolio"> <ul class="usa-sidenav usa-sidenav--portfolio">
<li class="usa-sidenav__item"> {% if has_domains_portfolio_permission %}
{% url 'portfolio-domains' portfolio.id as url %} <li class="usa-sidenav__item">
<a href="{{ url }}" {% if request.path == url %}class="usa-current"{% endif %}> {% url 'portfolio-domains' portfolio.id as url %}
Domains <a href="{{ url }}" {% if request.path == url %}class="usa-current"{% endif %}>
</a> Domains
</a>
</li> </li>
{% endif %}
{% if has_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 %}>
Domain requests Domain requests
</a> </a>
</li> </li>
{% endif %}
<li class="usa-sidenav__item"> <li class="usa-sidenav__item">
<a href="#"> <a href="#">
Members Members
</a> </a>
</li> </li>
<li class="usa-sidenav__item"> <li class="usa-sidenav__item">
<a href="#"> {% url 'portfolio-organization' portfolio.id as url %}
<a href="{{ url }}" {% if request.path == url %}class="usa-current"{% endif %}>
Organization Organization
</a> </a>
</li> </li>

View file

@ -1,39 +1,51 @@
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 waffle.decorators import flag_is_active from waffle.decorators import flag_is_active
from django.contrib.auth.decorators import login_required from django.views.generic import View
class PortfolioDomainsView(PortfolioDomainsPermissionView, View):
@login_required template_name = "portfolio_domains.html"
def portfolio_domains(request, portfolio_id):
context = {}
if request.user.is_authenticated: def get(self, request, portfolio_id):
# This is a django waffle flag which toggles features based off of the "flag" table context = {}
context["has_profile_feature_flag"] = flag_is_active(request, "profile_feature")
context["has_organization_feature_flag"] = flag_is_active(request, "organization_feature")
# Retrieve the portfolio object based on the provided portfolio_id if self.request.user.is_authenticated:
portfolio = get_object_or_404(Portfolio, id=portfolio_id) context["has_profile_feature_flag"] = flag_is_active(request, "profile_feature")
context["portfolio"] = portfolio context["has_organization_feature_flag"] = flag_is_active(request, "organization_feature")
portfolio = get_object_or_404(Portfolio, id=portfolio_id)
context["portfolio"] = portfolio
return render(request, "portfolio_domains.html", context) return render(request, "portfolio_domains.html", context)
class PortfolioDomainRequestsView(PortfolioDomainRequestsPermissionView, View):
@login_required template_name = "portfolio_requests.html"
def portfolio_domain_requests(request, portfolio_id):
context = {}
if request.user.is_authenticated: def get(self, request, portfolio_id):
# This is a django waffle flag which toggles features based off of the "flag" table context = {}
context["has_profile_feature_flag"] = flag_is_active(request, "profile_feature")
context["has_organization_feature_flag"] = flag_is_active(request, "organization_feature")
# Retrieve the portfolio object based on the provided portfolio_id if self.request.user.is_authenticated:
portfolio = get_object_or_404(Portfolio, id=portfolio_id) context["has_profile_feature_flag"] = flag_is_active(request, "profile_feature")
context["portfolio"] = portfolio context["has_organization_feature_flag"] = flag_is_active(request, "organization_feature")
portfolio = get_object_or_404(Portfolio, id=portfolio_id)
context["portfolio"] = portfolio
request.session["new_request"] = True
# This controls the creation of a new domain request in the wizard return render(request, "portfolio_requests.html", context)
request.session["new_request"] = True
return render(request, "portfolio_requests.html", context) class PortfolioOrganizationView(PortfolioOrganizationssPermissionView, View):
template_name = "portfolio_organization.html"
def get(self, request, portfolio_id):
context = {}
if self.request.user.is_authenticated:
context["has_profile_feature_flag"] = flag_is_active(request, "profile_feature")
context["has_organization_feature_flag"] = flag_is_active(request, "organization_feature")
portfolio = get_object_or_404(Portfolio, id=portfolio_id)
context["portfolio"] = portfolio
return render(request, "portfolio_organization.html", context)

View file

@ -2,6 +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.models import ( from registrar.models import (
Domain, Domain,
DomainRequest, DomainRequest,
@ -401,7 +402,7 @@ class UserProfilePermission(PermissionsLoginMixin):
return True return True
class PortfolioBasePermission(PermissionsLoginMixin): class PortfolioPermission(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"""
@ -417,10 +418,58 @@ class PortfolioBasePermission(PermissionsLoginMixin):
# portfolio_id = self.kwargs["pk"] # portfolio_id = self.kwargs["pk"]
# portfolio = Portfolio.objects.get(pk=portfolio_id) # portfolio = Portfolio.objects.get(pk=portfolio_id)
# The 'Base' portfolio permission is VIEW_PORTFOLIO if not has_base_portfolio_permission(self.request):
required_permission = User.UserPortfolioPermissionChoices.VIEW_PORTFOLIO return False
if not self.request.user.has_portfolio_permissions(required_permission): return True
class PortfolioDomainsPermission(PortfolioPermission):
"""
"""
def has_permission(self):
"""
"""
permission_dict = has_domains_portfolio_permission(self.request)
has_permission = permission_dict['has_domains_portfolio_permission']
if not has_permission:
return False
print('return true')
return True
class PortfolioDomainRequestsPermission(PortfolioPermission):
"""
"""
def has_permission(self):
"""
"""
permission_dict = has_requests_portfolio_permission(self.request)
has_permission = permission_dict['has_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:
return False return False
return True return True

View file

@ -13,9 +13,12 @@ from .mixins import (
DomainRequestPermissionWithdraw, DomainRequestPermissionWithdraw,
DomainInvitationPermission, DomainInvitationPermission,
DomainRequestWizardPermission, DomainRequestWizardPermission,
PortfolioDomainRequestsPermission,
PortfolioDomainsPermission,
PortfolioOrganizationssPermission,
UserDeleteDomainRolePermission, UserDeleteDomainRolePermission,
UserProfilePermission, UserProfilePermission,
PortfolioBasePermission, PortfolioPermission,
) )
import logging import logging
@ -166,7 +169,7 @@ class UserProfilePermissionView(UserProfilePermission, DetailView, abc.ABC):
raise NotImplementedError raise NotImplementedError
class PortfolioPermissionView(PortfolioBasePermission, DetailView, abc.ABC): class PortfolioBasePermissionView(PortfolioPermission, 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
@ -184,3 +187,17 @@ class PortfolioPermissionView(PortfolioBasePermission, DetailView, abc.ABC):
def template_name(self): def template_name(self):
raise NotImplementedError raise NotImplementedError
class PortfolioDomainsPermissionView(PortfolioDomainsPermission, PortfolioBasePermissionView, abc.ABC):
"""
"""
class PortfolioDomainRequestsPermissionView(PortfolioDomainRequestsPermission, PortfolioBasePermissionView, abc.ABC):
"""
"""
class PortfolioOrganizationssPermissionView(PortfolioOrganizationssPermission, PortfolioBasePermissionView, abc.ABC):
"""
"""