manage.get.gov/src/registrar/models/portfolio_invitation.py
zandercymatics 275ca470e8
linting
2024-11-22 15:06:18 -07:00

117 lines
4.1 KiB
Python

"""People are invited by email to administer domains."""
import logging
from django.db import models
from django_fsm import FSMField, transition
from django.contrib.auth import get_user_model
from registrar.models import DomainInvitation, UserPortfolioPermission
from .utility.portfolio_helper import (
UserPortfolioPermissionChoices,
UserPortfolioRoleChoices,
validate_portfolio_invitation,
) # type: ignore
from .utility.time_stamped_model import TimeStampedModel
from django.contrib.postgres.fields import ArrayField
logger = logging.getLogger(__name__)
class PortfolioInvitation(TimeStampedModel):
class Meta:
"""Contains meta information about this class"""
indexes = [
models.Index(fields=["status"]),
]
# Constants for status field
class PortfolioInvitationStatus(models.TextChoices):
INVITED = "invited", "Invited"
RETRIEVED = "retrieved", "Retrieved"
email = models.EmailField(
null=False,
blank=False,
)
portfolio = models.ForeignKey(
"registrar.Portfolio",
on_delete=models.CASCADE, # delete portfolio, then get rid of invitations
null=False,
related_name="portfolios",
)
roles = ArrayField(
models.CharField(
max_length=50,
choices=UserPortfolioRoleChoices.choices,
),
null=True,
blank=True,
help_text="Select one or more roles.",
)
additional_permissions = ArrayField(
models.CharField(
max_length=50,
choices=UserPortfolioPermissionChoices.choices,
),
null=True,
blank=True,
help_text="Select one or more additional permissions.",
)
status = FSMField(
choices=PortfolioInvitationStatus.choices,
default=PortfolioInvitationStatus.INVITED,
protected=True, # can't alter state except through transition methods!
)
def __str__(self):
return f"Invitation for {self.email} on {self.portfolio} is {self.status}"
def get_managed_domains_count(self):
"""Return the count of domain invitations managed by the invited user for this portfolio."""
# Filter the UserDomainRole model to get domains where the user has a manager role
managed_domains = DomainInvitation.objects.filter(
email=self.email, domain__domain_info__portfolio=self.portfolio
).count()
return managed_domains
def get_portfolio_permissions(self):
"""
Retrieve the permissions for the user's portfolio roles from the invite.
"""
return UserPortfolioPermission.get_portfolio_permissions(self.roles, self.additional_permissions)
@transition(field="status", source=PortfolioInvitationStatus.INVITED, target=PortfolioInvitationStatus.RETRIEVED)
def retrieve(self):
"""When an invitation is retrieved, create the corresponding permission.
Raises:
RuntimeError if no matching user can be found.
"""
# get a user with this email address
User = get_user_model()
try:
user = User.objects.get(email=self.email)
except User.DoesNotExist:
# should not happen because a matching user should exist before
# we retrieve this invitation
raise RuntimeError("Cannot find the user to retrieve this portfolio invitation.")
# and create a role for that user on this portfolio
user_portfolio_permission, _ = UserPortfolioPermission.objects.get_or_create(
portfolio=self.portfolio, user=user
)
if self.roles and len(self.roles) > 0:
user_portfolio_permission.roles = self.roles
if self.additional_permissions and len(self.additional_permissions) > 0:
user_portfolio_permission.additional_permissions = self.additional_permissions
user_portfolio_permission.save()
def clean(self):
"""Extends clean method to perform additional validation, which can raise errors in django admin."""
super().clean()
validate_portfolio_invitation(self)