🗃️(models) create abstract BaseInvitation and DomainInvitation models

create abstract BaseInvitation models to factorize common elements between
existing Invitation model team-side and new DomainInvitation model
This commit is contained in:
Marie PUPO JEAMMET
2025-02-07 18:54:10 +01:00
committed by Sabrina Demagny
parent 7ea381c88a
commit 9ee1ef5ba0
3 changed files with 149 additions and 49 deletions

View File

@@ -895,18 +895,10 @@ class TeamWebhook(BaseModel):
return headers
class Invitation(BaseModel):
"""User invitation to teams."""
class BaseInvitation(BaseModel):
"""Abstract base invitation model, surcharged for teams or domains."""
email = models.EmailField(_("email address"), null=False, blank=False)
team = models.ForeignKey(
Team,
on_delete=models.CASCADE,
related_name="invitations",
)
role = models.CharField(
max_length=20, choices=RoleChoices.choices, default=RoleChoices.MEMBER
)
issuer = models.ForeignKey(
User,
on_delete=models.CASCADE,
@@ -914,17 +906,7 @@ class Invitation(BaseModel):
)
class Meta:
db_table = "people_invitation"
verbose_name = _("Team invitation")
verbose_name_plural = _("Team invitations")
constraints = [
models.UniqueConstraint(
fields=["email", "team"], name="email_and_team_unique_together"
)
]
def __str__(self):
return f"{self.email} invited to {self.team}"
abstract = True
def save(self, *args, **kwargs):
"""Make invitations read-only."""
@@ -953,31 +935,6 @@ class Invitation(BaseModel):
validity_duration = timedelta(seconds=settings.INVITATION_VALIDITY_DURATION)
return timezone.now() > (self.created_at + validity_duration)
def get_abilities(self, user):
"""Compute and return abilities for a given user."""
can_delete = False
role = None
if user.is_authenticated:
try:
role = self.user_role
except AttributeError:
try:
role = self.team.accesses.filter(user=user).values("role")[0][
"role"
]
except (self._meta.model.DoesNotExist, IndexError):
role = None
can_delete = role in [RoleChoices.OWNER, RoleChoices.ADMIN]
return {
"delete": can_delete,
"get": bool(role),
"patch": False,
"put": False,
}
def email_invitation(self):
"""Email invitation to the user."""
try:
@@ -1002,5 +959,52 @@ class Invitation(BaseModel):
logger.error("invitation to %s was not sent: %s", self.email, exception)
# It's not clear yet how best to split this file.
# pylint: disable=C0302
class Invitation(BaseInvitation):
"""User invitation to teams."""
team = models.ForeignKey(
Team,
on_delete=models.CASCADE,
related_name="invitations",
)
role = models.CharField(
max_length=20, choices=RoleChoices.choices, default=RoleChoices.MEMBER
)
class Meta:
db_table = "people_invitation"
verbose_name = _("Team invitation")
verbose_name_plural = _("Team invitations")
constraints = [
models.UniqueConstraint(
fields=["email", "team"], name="email_and_team_unique_together"
)
]
def __str__(self):
return f"{self.email} invited to {self.team}"
def get_abilities(self, user):
"""Compute and return abilities for a given user."""
can_delete = False
role = None
if user.is_authenticated:
try:
role = self.user_role
except AttributeError:
try:
role = self.team.accesses.filter(user=user).values("role")[0][
"role"
]
except (self._meta.model.DoesNotExist, IndexError):
role = None
can_delete = role in [RoleChoices.OWNER, RoleChoices.ADMIN]
return {
"delete": can_delete,
"get": bool(role),
"patch": False,
"put": False,
}

View File

@@ -0,0 +1,35 @@
# Generated by Django 5.1.5 on 2025-02-07 17:27
import django.db.models.deletion
import uuid
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mailbox_manager', '0023_mailbox_email_mailbox_last_login_mailbox_password'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='DomainInvitation',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='primary key for the record as UUID', primary_key=True, serialize=False, verbose_name='id')),
('created_at', models.DateTimeField(auto_now_add=True, help_text='date and time at which a record was created', verbose_name='created at')),
('updated_at', models.DateTimeField(auto_now=True, help_text='date and time at which a record was last updated', verbose_name='updated at')),
('email', models.EmailField(max_length=254, verbose_name='email address')),
('role', models.CharField(choices=[('viewer', 'Viewer'), ('administrator', 'Administrator'), ('owner', 'Owner')], default='viewer', max_length=20)),
('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='domain_invitations', to='mailbox_manager.maildomain')),
('issuer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='domain_invitations', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'Domain invitation',
'verbose_name_plural': 'Domain invitations',
'db_table': 'people_domain_invitation',
'constraints': [models.UniqueConstraint(fields=('email', 'domain'), name='email_and_domain_unique_together')],
},
),
]

View File

@@ -9,7 +9,7 @@ from django.db import models
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _
from core.models import BaseModel, Organization
from core.models import BaseInvitation, BaseModel, Organization, User
from mailbox_manager.enums import (
MailboxStatusChoices,
@@ -273,3 +273,64 @@ class Mailbox(AbstractBaseUser, BaseModel):
def get_email(self):
"""Return the email address of the mailbox."""
return f"{self.local_part}@{self.domain.name}"
class DomainInvitation(BaseInvitation):
"""User invitation to teams."""
issuer = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name="domain_invitations",
)
domain = models.ForeignKey(
MailDomain,
on_delete=models.CASCADE,
related_name="domain_invitations",
)
role = models.CharField(
max_length=20,
choices=MailDomainRoleChoices.choices,
default=MailDomainRoleChoices.VIEWER,
)
class Meta:
db_table = "people_domain_invitation"
verbose_name = _("Domain invitation")
verbose_name_plural = _("Domain invitations")
constraints = [
models.UniqueConstraint(
fields=["email", "domain"], name="email_and_domain_unique_together"
)
]
def __str__(self):
return f"{self.email} invited to {self.domain}"
def get_abilities(self, user):
"""Compute and return abilities for a given user."""
can_delete = False
role = None
if user.is_authenticated:
try:
role = self.user_role
except AttributeError:
try:
role = self.domain.accesses.filter(user=user).values("role")[0][
"role"
]
except (self._meta.model.DoesNotExist, IndexError):
role = None
can_delete = role in [
MailDomainRoleChoices.OWNER,
MailDomainRoleChoices.ADMIN,
]
return {
"delete": can_delete,
"get": bool(role),
"patch": False,
"put": False,
}