PR suggestions

This commit is contained in:
zandercymatics 2024-12-18 12:25:21 -07:00
parent a9923f4cc9
commit f19ff3cd66
No known key found for this signature in database
GPG key ID: FF4636ABEC9682B7
5 changed files with 22 additions and 34 deletions

View file

@ -4004,10 +4004,6 @@ class WaffleFlagAdmin(FlagAdmin):
if extra_context is None: if extra_context is None:
extra_context = {} extra_context = {}
extra_context["dns_prototype_flag"] = flag_is_active_for_user(request.user, "dns_prototype_flag") extra_context["dns_prototype_flag"] = flag_is_active_for_user(request.user, "dns_prototype_flag")
# Normally you have to first enable the org feature then navigate to an org before you see these.
# Lets just auto-populate it on page load to make development easier.
extra_context["organization_members"] = flag_is_active_for_user(request.user, "organization_members")
extra_context["organization_requests"] = flag_is_active_for_user(request.user, "organization_requests")
return super().changelist_view(request, extra_context=extra_context) return super().changelist_view(request, extra_context=extra_context)

View file

@ -13,4 +13,5 @@ from .domain import (
) )
from .portfolio import ( from .portfolio import (
PortfolioOrgAddressForm, PortfolioOrgAddressForm,
PortfolioMemberForm,
) )

View file

@ -273,7 +273,11 @@ class NewMemberForm(forms.ModelForm):
del self.errors[admin_member_error] del self.errors[admin_member_error]
return cleaned_data return cleaned_data
class BasePortfolioMemberForm(forms.Form): class BasePortfolioMemberForm(forms.Form):
"""Base form for the PortfolioMemberForm and PortfolioInvitedMemberForm"""
# The label for each of these has a red "required" star. We can just embed that here for simplicity.
required_star = '<abbr class="usa-hint usa-hint--required" title="required">*</abbr>' required_star = '<abbr class="usa-hint usa-hint--required" title="required">*</abbr>'
role = forms.ChoiceField( role = forms.ChoiceField(
choices=[ choices=[
@ -354,25 +358,11 @@ class BasePortfolioMemberForm(forms.Form):
} }
def clean(self): def clean(self):
""" """Validates form data based on selected role and its required fields."""
Validates form data based on selected role and its required fields.
Since form fields are dynamically shown/hidden via JavaScript based on role selection,
we only validate fields that are relevant to the selected role:
- organization_admin: ["member_permission_admin", "domain_request_permission_admin"]
- organization_member: ["domain_request_permission_member"]
This ensures users aren't required to fill out hidden fields and maintains
proper validation based on their role selection.
NOTE: This page uses ROLE_REQUIRED_FIELDS for the aforementioned mapping.
Raises:
ValueError: If ROLE_REQUIRED_FIELDS references a non-existent form field
"""
cleaned_data = super().clean() cleaned_data = super().clean()
role = cleaned_data.get("role") role = cleaned_data.get("role")
# Get required fields for the selected role. # Get required fields for the selected role. Then validate all required fields for the role.
# Then validate all required fields for the role.
required_fields = self.ROLE_REQUIRED_FIELDS.get(role, []) required_fields = self.ROLE_REQUIRED_FIELDS.get(role, [])
for field_name in required_fields: for field_name in required_fields:
# Helpful error for if this breaks # Helpful error for if this breaks
@ -394,15 +384,17 @@ class BasePortfolioMemberForm(forms.Form):
self.instance.save() self.instance.save()
return self.instance return self.instance
# Explanation of how map_instance_to_form / map_cleaned_data_to_instance work:
# map_instance_to_form => called on init to set self.instance.
# Converts the incoming object (usually PortfolioInvitation or UserPortfolioPermission)
# into a dictionary representation for the form to use automatically.
# map_cleaned_data_to_instance => called on save() to save the instance to the db.
# Takes the self.cleaned_data dict, and converts this dict back to the object.
def map_instance_to_form(self, instance): def map_instance_to_form(self, instance):
""" """
Maps user instance to form fields, handling roles and permissions. Maps user instance to form fields, handling roles and permissions.
Determines:
- User's role (admin vs member)
- Domain request permissions (EDIT_REQUESTS, VIEW_ALL_REQUESTS, or "no_access")
- Member management permissions (EDIT_MEMBERS or VIEW_MEMBERS)
Returns form data dictionary with appropriate permission levels based on user role: Returns form data dictionary with appropriate permission levels based on user role:
{ {
"role": "organization_admin" or "organization_member", "role": "organization_admin" or "organization_member",
@ -462,13 +454,6 @@ class BasePortfolioMemberForm(forms.Form):
def map_cleaned_data_to_instance(self, cleaned_data, instance): def map_cleaned_data_to_instance(self, cleaned_data, instance):
""" """
Maps cleaned data to a member instance, setting roles and permissions. Maps cleaned data to a member instance, setting roles and permissions.
Additional permissions logic:
- For org admins: Adds domain request and member admin permissions if selected
- For other roles: Adds domain request member permissions if not 'no_access'
- Automatically adds VIEW permissions when EDIT permissions are granted
- Filters out permissions already granted by base role
Args: Args:
cleaned_data (dict): Cleaned data containing role and permission choices cleaned_data (dict): Cleaned data containing role and permission choices
instance: Instance to update instance: Instance to update

View file

@ -111,7 +111,12 @@ class UserPortfolioPermission(TimeStampedModel):
@classmethod @classmethod
def get_portfolio_permissions(cls, roles, additional_permissions, get_list=True): def get_portfolio_permissions(cls, roles, additional_permissions, get_list=True):
"""Class method to return a list of permissions based on roles and addtl permissions""" """Class method to return a list of permissions based on roles and addtl permissions.
Params:
roles => An array of roles
additional_permissions => An array of additional_permissions
get_list => If true, returns a list of perms. If false, returns a set of perms.
"""
# Use a set to avoid duplicate permissions # Use a set to avoid duplicate permissions
portfolio_permissions = set() portfolio_permissions = set()
if roles: if roles:

View file

@ -1,6 +1,7 @@
"""View classes that enforce authorization.""" """View classes that enforce authorization."""
import abc # abstract base class import abc # abstract base class
from django.views.generic import DetailView, DeleteView, TemplateView, UpdateView from django.views.generic import DetailView, DeleteView, TemplateView, UpdateView
from registrar.models import Domain, DomainRequest, DomainInvitation, Portfolio from registrar.models import Domain, DomainRequest, DomainInvitation, Portfolio
from registrar.models.user import User from registrar.models.user import User