🗃️(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:
committed by
Sabrina Demagny
parent
7ea381c88a
commit
9ee1ef5ba0
@@ -895,18 +895,10 @@ class TeamWebhook(BaseModel):
|
|||||||
return headers
|
return headers
|
||||||
|
|
||||||
|
|
||||||
class Invitation(BaseModel):
|
class BaseInvitation(BaseModel):
|
||||||
"""User invitation to teams."""
|
"""Abstract base invitation model, surcharged for teams or domains."""
|
||||||
|
|
||||||
email = models.EmailField(_("email address"), null=False, blank=False)
|
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(
|
issuer = models.ForeignKey(
|
||||||
User,
|
User,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
@@ -914,17 +906,7 @@ class Invitation(BaseModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "people_invitation"
|
abstract = True
|
||||||
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 save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""Make invitations read-only."""
|
"""Make invitations read-only."""
|
||||||
@@ -953,31 +935,6 @@ class Invitation(BaseModel):
|
|||||||
validity_duration = timedelta(seconds=settings.INVITATION_VALIDITY_DURATION)
|
validity_duration = timedelta(seconds=settings.INVITATION_VALIDITY_DURATION)
|
||||||
return timezone.now() > (self.created_at + 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):
|
def email_invitation(self):
|
||||||
"""Email invitation to the user."""
|
"""Email invitation to the user."""
|
||||||
try:
|
try:
|
||||||
@@ -1002,5 +959,52 @@ class Invitation(BaseModel):
|
|||||||
logger.error("invitation to %s was not sent: %s", self.email, exception)
|
logger.error("invitation to %s was not sent: %s", self.email, exception)
|
||||||
|
|
||||||
|
|
||||||
# It's not clear yet how best to split this file.
|
class Invitation(BaseInvitation):
|
||||||
# pylint: disable=C0302
|
"""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,
|
||||||
|
}
|
||||||
|
|||||||
@@ -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')],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -9,7 +9,7 @@ from django.db import models
|
|||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from django.utils.translation import gettext_lazy as _
|
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 (
|
from mailbox_manager.enums import (
|
||||||
MailboxStatusChoices,
|
MailboxStatusChoices,
|
||||||
@@ -273,3 +273,64 @@ class Mailbox(AbstractBaseUser, BaseModel):
|
|||||||
def get_email(self):
|
def get_email(self):
|
||||||
"""Return the email address of the mailbox."""
|
"""Return the email address of the mailbox."""
|
||||||
return f"{self.local_part}@{self.domain.name}"
|
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,
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user