This commit is contained in:
David Kennedy 2024-08-20 15:29:28 -04:00
parent 432ee9c860
commit 3c89557976
No known key found for this signature in database
GPG key ID: 6528A5386E66B96B
12 changed files with 80 additions and 103 deletions

View file

@ -9,8 +9,6 @@ from django.http import HttpResponseRedirect
from django.shortcuts import redirect
from urllib.parse import parse_qs, urlencode
from waffle import flag_is_active
from djangooidc.oidc import Client
from djangooidc import exceptions as o_e
from registrar.models import User
@ -113,13 +111,6 @@ def login_callback(request):
if not user.verification_type or is_fixture_user:
user.set_user_verification_type()
user.save()
if not flag_is_active(request, "multiple_portfolios"):
user.set_default_last_selected_portfolio()
user.save()
else:
user.last_selected_portfolio = None
user.save()
login(request, user)
logger.info("Successfully logged in user %s" % user)

View file

@ -717,17 +717,12 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
"is_superuser",
"groups",
"user_permissions",
"last_selected_portfolio",
)
},
),
("Important dates", {"fields": ("last_login", "date_joined")}),
)
autocomplete_fields = [
"last_selected_portfolio",
]
readonly_fields = ("verification_type",)
analyst_fieldsets = (
@ -747,7 +742,6 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
"fields": (
"is_active",
"groups",
"last_selected_portfolio",
)
},
),
@ -802,7 +796,6 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
"Important dates",
"last_login",
"date_joined",
"last_selected_portfolio",
]
# TODO: delete after we merge organization feature

View file

@ -61,27 +61,34 @@ def add_has_profile_feature_flag_to_context(request):
def portfolio_permissions(request):
"""Make portfolio permissions for the request user available in global context"""
try:
if not request.user or not request.user.is_authenticated or not flag_is_active(request, "organization_feature"):
if request.session["portfolio"] is not None:
return {
"has_base_portfolio_permission": False,
"has_domains_portfolio_permission": False,
"has_domain_requests_portfolio_permission": False,
"portfolio": None,
"has_organization_feature_flag": False,
"has_base_portfolio_permission": request.user.has_base_portfolio_permission(request.session["portfolio"]),
"has_domains_portfolio_permission": request.user.has_domains_portfolio_permission(request.session["portfolio"]),
"has_domain_requests_portfolio_permission": request.user.has_domain_requests_portfolio_permission(request.session["portfolio"]),
"has_view_suborganization": request.user.has_view_suborganization(request.session["portfolio"]),
"has_edit_suborganization": request.user.has_edit_suborganization(request.session["portfolio"]),
"portfolio": request.session["portfolio"],
"has_organization_feature_flag": True,
}
return {
"has_base_portfolio_permission": request.user.has_base_portfolio_permission(),
"has_domains_portfolio_permission": request.user.has_domains_portfolio_permission(),
"has_domain_requests_portfolio_permission": request.user.has_domain_requests_portfolio_permission(),
"portfolio": request.user.last_selected_portfolio,
"has_organization_feature_flag": True,
"has_base_portfolio_permission": False,
"has_domains_portfolio_permission": False,
"has_domain_requests_portfolio_permission": False,
"has_view_suborganization": False,
"has_edit_suborganization": False,
"portfolio": None,
"has_organization_feature_flag": False,
}
except AttributeError:
# Handles cases where request.user might not exist
return {
"has_base_portfolio_permission": False,
"has_domains_portfolio_permission": False,
"has_domain_requests_portfolio_permission": False,
"has_view_suborganization": False,
"has_edit_suborganization": False,
"portfolio": None,
"has_organization_feature_flag": False,
}

View file

@ -25,17 +25,6 @@ class Migration(migrations.Migration):
model_name="user",
name="portfolio_roles",
),
migrations.AddField(
model_name="user",
name="last_selected_portfolio",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="portfolio_selected_by_users",
to="registrar.portfolio",
),
),
migrations.CreateModel(
name="UserPortfolioPermission",
fields=[

View file

@ -110,14 +110,6 @@ class User(AbstractUser):
related_name="users",
)
last_selected_portfolio = models.ForeignKey(
"registrar.Portfolio",
null=True,
blank=True,
related_name="portfolio_selected_by_users",
on_delete=models.SET_NULL,
)
phone = PhoneNumberField(
null=True,
blank=True,
@ -208,50 +200,48 @@ class User(AbstractUser):
def has_contact_info(self):
return bool(self.title or self.email or self.phone)
def _has_portfolio_permission(self, portfolio_permission):
def _has_portfolio_permission(self, portfolio, portfolio_permission):
"""The views should only call this function when testing for perms and not rely on roles."""
if not self.last_selected_portfolio:
return False
portfolio_perms = self.portfolio_permissions.filter(portfolio=self.last_selected_portfolio, user=self).first()
portfolio_perms = self.portfolio_permissions.filter(portfolio=portfolio, user=self).first()
if not portfolio_perms:
return False
portfolio_permissions = portfolio_perms._get_portfolio_permissions()
return portfolio_permission in portfolio_permissions
def has_base_portfolio_permission(self):
return self._has_portfolio_permission(UserPortfolioPermissionChoices.VIEW_PORTFOLIO)
def has_base_portfolio_permission(self, portfolio):
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.VIEW_PORTFOLIO)
def has_edit_org_portfolio_permission(self):
return self._has_portfolio_permission(UserPortfolioPermissionChoices.EDIT_PORTFOLIO)
def has_edit_org_portfolio_permission(self, portfolio):
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.EDIT_PORTFOLIO)
def has_domains_portfolio_permission(self):
return self._has_portfolio_permission(
def has_domains_portfolio_permission(self, portfolio):
return self._has_portfolio_permission(portfolio,
UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS
) or self._has_portfolio_permission(UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS)
) or self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS)
def has_domain_requests_portfolio_permission(self):
return self._has_portfolio_permission(
def has_domain_requests_portfolio_permission(self, portfolio):
return self._has_portfolio_permission(portfolio,
UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS
) or self._has_portfolio_permission(UserPortfolioPermissionChoices.VIEW_CREATED_REQUESTS)
) or self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.VIEW_CREATED_REQUESTS)
def has_view_all_domains_permission(self):
def has_view_all_domains_permission(self, portfolio):
"""Determines if the current user can view all available domains in a given portfolio"""
return self._has_portfolio_permission(UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS)
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS)
# Field specific permission checks
def has_view_suborganization(self):
return self._has_portfolio_permission(UserPortfolioPermissionChoices.VIEW_SUBORGANIZATION)
def has_view_suborganization(self, portfolio):
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.VIEW_SUBORGANIZATION)
def has_edit_suborganization(self):
return self._has_portfolio_permission(UserPortfolioPermissionChoices.EDIT_SUBORGANIZATION)
def has_edit_suborganization(self, portfolio):
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.EDIT_SUBORGANIZATION)
def set_default_last_selected_portfolio(self):
def get_first_portfolio(self):
permission = self.portfolio_permissions.first()
if permission:
self.last_selected_portfolio = permission.portfolio
return permission.portfolio
return None
@classmethod
def needs_identity_verification(cls, email, uuid):
@ -367,7 +357,7 @@ class User(AbstractUser):
email__iexact=self.email, status=PortfolioInvitation.PortfolioInvitationStatus.INVITED
):
only_single_portfolio = (
not flag_is_active(None, "multiple_portfolios") and self.last_selected_portfolio is None
not flag_is_active(None, "multiple_portfolios") and self.get_first_portfolio() is None
)
if only_single_portfolio or flag_is_active(None, "multiple_portfolios"):
try:
@ -396,12 +386,12 @@ class User(AbstractUser):
def is_org_user(self, request):
has_organization_feature_flag = flag_is_active(request, "organization_feature")
return has_organization_feature_flag and self.has_base_portfolio_permission()
return has_organization_feature_flag and self.has_base_portfolio_permission(request.session["portfolio"])
def get_user_domain_ids(self, request):
"""Returns either the domains ids associated with this user on UserDomainRole or Portfolio"""
if self.is_org_user(request) and self.has_view_all_domains_permission():
return DomainInformation.objects.filter(portfolio=self.last_selected_portfolio).values_list(
if self.is_org_user(request) and self.has_view_all_domains_permission(request.session["portfolio"]):
return DomainInformation.objects.filter(portfolio=request.session["portfolio"]).values_list(
"domain_id", flat=True
)
else:

View file

@ -125,8 +125,9 @@ class CheckUserProfileMiddleware:
class CheckPortfolioMiddleware:
"""
Checks if the current user has a portfolio
If they do, redirect them to the portfolio homepage when they navigate to home.
this middleware should serve two purposes:
1 - set the portfolio in session if appropriate # views will need the session portfolio
2 - if path is home and session portfolio is set, redirect based on permissions of user
"""
def __init__(self, get_response):
@ -140,20 +141,28 @@ class CheckPortfolioMiddleware:
def process_view(self, request, view_func, view_args, view_kwargs):
current_path = request.path
if current_path == self.home and request.user.is_authenticated and request.user.is_org_user(request):
if not request.user.is_authenticated:
return None
# set the portfolio in the session if it is not set
if not "portfolio" in request.session or request.session["portfolio"] is None:
# if user is a multiple portfolio
if flag_is_active(request, "multiple_portfolios"):
# NOTE: we will want to change later to have a workflow for selecting
# portfolio and another for switching portfolio; for now, select first
request.session["portfolio"] = request.user.get_first_portfolio()
elif flag_is_active(request, "organization_feature"):
request.session["portfolio"] = request.user.get_first_portfolio()
else:
request.session["portfolio"] = None
if request.user.has_base_portfolio_permission():
portfolio = request.user.last_selected_portfolio
# Add the portfolio to the request object
request.last_selected_portfolio = portfolio
if request.user.has_domains_portfolio_permission():
if request.session["portfolio"] is not None and current_path == self.home:
if request.user.has_base_portfolio_permission(request.session["portfolio"]):
if request.user.has_domains_portfolio_permission(request.session["portfolio"]):
portfolio_redirect = reverse("domains")
else:
# View organization is the lowest access
portfolio_redirect = reverse("organization")
return HttpResponseRedirect(portfolio_redirect)
return None

View file

@ -72,7 +72,7 @@
{% include "includes/summary_item.html" with title='DNSSEC' value='Not Enabled' edit_link=url editable=is_editable %}
{% endif %}
{% if portfolio and has_domains_portfolio_permission and request.user.has_view_suborganization %}
{% if portfolio and has_domains_portfolio_permission and has_view_suborganization %}
{% url 'domain-suborganization' pk=domain.id as url %}
{% include "includes/summary_item.html" with title='Suborganization' value=domain.domain_info.sub_organization edit_link=url editable=is_editable|and:request.user.has_edit_suborganization %}
{% else %}

View file

@ -61,7 +61,7 @@
{% if portfolio %}
{% comment %} Only show this menu option if the user has the perms to do so {% endcomment %}
{% if has_domains_portfolio_permission and request.user.has_view_suborganization %}
{% if has_domains_portfolio_permission and has_view_suborganization %}
{% with url_name="domain-suborganization" %}
{% include "includes/domain_sidenav_item.html" with item_text="Suborganization" %}
{% endwith %}

View file

@ -157,7 +157,7 @@
<th data-sortable="name" scope="col" role="columnheader">Domain name</th>
<th data-sortable="expiration_date" scope="col" role="columnheader">Expires</th>
<th data-sortable="state_display" scope="col" role="columnheader">Status</th>
{% if has_domains_portfolio_permission and request.user.has_view_suborganization %}
{% if has_domains_portfolio_permission and has_view_suborganization %}
<th data-sortable="suborganization" scope="col" role="columnheader">Suborganization</th>
{% endif %}
<th

View file

@ -177,7 +177,7 @@ class DomainView(DomainBaseView):
if self.request.user.has_domains_portfolio_permission():
if Domain.objects.filter(id=pk).exists():
domain = Domain.objects.get(id=pk)
if domain.domain_info.portfolio == self.request.user.last_selected_portfolio:
if domain.domain_info.portfolio == self.request.session["portfolio"]:
return True
return False
@ -236,7 +236,7 @@ class DomainOrgNameAddressView(DomainFormBaseView):
# Org users shouldn't have access to this page
is_org_user = self.request.user.is_org_user(self.request)
if self.request.user.last_selected_portfolio and is_org_user:
if self.request.session["portfolio"] and is_org_user:
return False
else:
return super().has_permission()
@ -255,7 +255,7 @@ class DomainSubOrganizationView(DomainFormBaseView):
# non-org users shouldn't have access to this page
is_org_user = self.request.user.is_org_user(self.request)
if self.request.user.last_selected_portfolio and is_org_user:
if self.request.session["portfolio"] and is_org_user:
return super().has_permission()
else:
return False
@ -335,7 +335,7 @@ class DomainSeniorOfficialView(DomainFormBaseView):
# Org users shouldn't have access to this page
is_org_user = self.request.user.is_org_user(self.request)
if self.request.user.last_selected_portfolio and is_org_user:
if self.request.session["portfolio"] and is_org_user:
return False
else:
return super().has_permission()

View file

@ -51,15 +51,14 @@ class PortfolioOrganizationView(PortfolioBasePermissionView, FormMixin):
def get_context_data(self, **kwargs):
"""Add additional context data to the template."""
context = super().get_context_data(**kwargs)
context["has_edit_org_portfolio_permission"] = self.request.user.has_edit_org_portfolio_permission()
context["has_edit_org_portfolio_permission"] = self.request.user.has_edit_org_portfolio_permission(self.request.session["portfolio"])
return context
def get_object(self, queryset=None):
"""Get the portfolio object based on the request user."""
portfolio = self.request.user.last_selected_portfolio
if portfolio is None:
"""Get the portfolio object based on the session."""
if self.request.session["portfolio"] is None:
raise Http404("No organization found for this user")
return portfolio
return self.request.session["portfolio"]
def get_form_kwargs(self):
"""Include the instance in the form kwargs."""
@ -111,11 +110,10 @@ class PortfolioSeniorOfficialView(PortfolioBasePermissionView, FormMixin):
context_object_name = "portfolio"
def get_object(self, queryset=None):
"""Get the portfolio object based on the request user."""
portfolio = self.request.user.last_selected_portfolio
if portfolio is None:
"""Get the portfolio object based on the session."""
if self.request.session["portfolio"] is None:
raise Http404("No organization found for this user")
return portfolio
return self.request.session["portfolio"]
def get_form_kwargs(self):
"""Include the instance in the form kwargs."""

View file

@ -419,7 +419,7 @@ class PortfolioBasePermission(PermissionsLoginMixin):
if not self.request.user.is_authenticated:
return False
return self.request.user.has_base_portfolio_permission()
return self.request.user.has_base_portfolio_permission(self.request.session["portfolio"])
class PortfolioDomainsPermission(PortfolioBasePermission):
@ -434,7 +434,7 @@ class PortfolioDomainsPermission(PortfolioBasePermission):
if not self.request.user.is_authenticated:
return False
return self.request.user.has_domains_portfolio_permission()
return self.request.user.has_domains_portfolio_permission(self.request.session["portfolio"])
class PortfolioDomainRequestsPermission(PortfolioBasePermission):
@ -449,4 +449,4 @@ class PortfolioDomainRequestsPermission(PortfolioBasePermission):
if not self.request.user.is_authenticated:
return False
return self.request.user.has_domain_requests_portfolio_permission()
return self.request.user.has_domain_requests_portfolio_permission(self.request.session["portfolio"])