🐛(mailbox) fix case-sensitive duplicate display names
uniqueness on first name + last name was case-sensitive, which allowed duplicates
This commit is contained in:
committed by
Marie
parent
302671bc69
commit
71a7bf688f
@@ -8,6 +8,7 @@ and this project adheres to
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
- 🐛(mailbox) fix case-sensitive duplicates on display names
|
||||||
- ✨(mailbox) synchronize password of newly created mailbox with Dimail's
|
- ✨(mailbox) synchronize password of newly created mailbox with Dimail's
|
||||||
|
|
||||||
## [1.19.1] - 2025-09-19
|
## [1.19.1] - 2025-09-19
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ class MailboxAdmin(UserAdmin):
|
|||||||
|
|
||||||
list_display = ("__str__", "domain", "status", "updated_at")
|
list_display = ("__str__", "domain", "status", "updated_at")
|
||||||
list_filter = ("status",)
|
list_filter = ("status",)
|
||||||
search_fields = ("local_part", "domain__name")
|
search_fields = ("local_part", "domain__name", "first_name", "last_name")
|
||||||
readonly_fields = ["updated_at"]
|
readonly_fields = ["updated_at"]
|
||||||
|
|
||||||
fieldsets = None
|
fieldsets = None
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-10-21 15:49
|
||||||
|
|
||||||
|
import django.db.models.functions.text
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('mailbox_manager', '0026_alter_mailbox_unique_together_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveConstraint(
|
||||||
|
model_name='mailbox',
|
||||||
|
name='unique_ox_display_name',
|
||||||
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name='mailbox',
|
||||||
|
constraint=models.UniqueConstraint(django.db.models.functions.text.Lower('first_name'), django.db.models.functions.text.Lower('last_name'), models.F('domain'), name='unique_ox_display_name', violation_error_message='Mailbox with this First name, Last name and Domain already exists.'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -10,6 +10,7 @@ from django.contrib.auth.base_user import AbstractBaseUser
|
|||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.core import exceptions, mail, validators
|
from django.core import exceptions, mail, validators
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models.functions import Lower
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from django.utils.translation import get_language, gettext, override
|
from django.utils.translation import get_language, gettext, override
|
||||||
@@ -308,12 +309,17 @@ class Mailbox(AbstractBaseUser, BaseModel):
|
|||||||
fields=["local_part", "domain"], name="unique_username"
|
fields=["local_part", "domain"], name="unique_username"
|
||||||
),
|
),
|
||||||
models.UniqueConstraint(
|
models.UniqueConstraint(
|
||||||
fields=["first_name", "last_name", "domain"],
|
Lower("first_name"),
|
||||||
|
Lower("last_name"),
|
||||||
|
"domain",
|
||||||
name="unique_ox_display_name",
|
name="unique_ox_display_name",
|
||||||
|
violation_error_message="Mailbox with this First name, \
|
||||||
|
Last name and Domain already exists.",
|
||||||
),
|
),
|
||||||
# Display name in OpenXChange must be unique
|
# Display name in OpenXChange must be unique
|
||||||
# To avoid sending failing requests to dimail,
|
# To avoid sending failing requests to dimail,
|
||||||
# we impose uniqueness here too
|
# we impose uniqueness here too
|
||||||
|
# And compare to lowercase to enforce case-insensitive uniqueness
|
||||||
]
|
]
|
||||||
ordering = ["-created_at"]
|
ordering = ["-created_at"]
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ def test_api_mailboxes__create_viewer_failure(mailbox_data):
|
|||||||
def test_api_mailboxes__create_display_name_must_be_unique():
|
def test_api_mailboxes__create_display_name_must_be_unique():
|
||||||
"""Primary id on OpenXchange is display name (first_name + last_name).
|
"""Primary id on OpenXchange is display name (first_name + last_name).
|
||||||
It needs to be unique on each context. We don't track context info for now
|
It needs to be unique on each context. We don't track context info for now
|
||||||
but can impose uniqueness by domain."""
|
but can impose case-insensitive uniqueness by domain."""
|
||||||
access = factories.MailDomainAccessFactory(role=enums.MailDomainRoleChoices.OWNER)
|
access = factories.MailDomainAccessFactory(role=enums.MailDomainRoleChoices.OWNER)
|
||||||
existing_mailbox = factories.MailboxFactory(domain=access.domain)
|
existing_mailbox = factories.MailboxFactory(domain=access.domain)
|
||||||
|
|
||||||
@@ -86,8 +86,8 @@ def test_api_mailboxes__create_display_name_must_be_unique():
|
|||||||
|
|
||||||
new_mailbox_data = {
|
new_mailbox_data = {
|
||||||
"local_part": "something_else",
|
"local_part": "something_else",
|
||||||
"first_name": existing_mailbox.first_name,
|
"first_name": existing_mailbox.first_name.upper(), # ensure case-insensitivity
|
||||||
"last_name": existing_mailbox.last_name,
|
"last_name": existing_mailbox.last_name.lower(),
|
||||||
}
|
}
|
||||||
response = client.post(
|
response = client.post(
|
||||||
f"/api/v1.0/mail-domains/{access.domain.slug}/mailboxes/",
|
f"/api/v1.0/mail-domains/{access.domain.slug}/mailboxes/",
|
||||||
|
|||||||
Reference in New Issue
Block a user