🐛(aliases) alias destination can be devnull@devnull
devnull@devnull is not considered a valid email address by django's EmailFieldValidator but it's a special address in dimail's config. Make "destination" a CharField instead of an EmailField to replace validator and add devnull to allowlist.
This commit is contained in:
committed by
Marie
parent
5ebc88bcff
commit
99433a6722
@@ -8,6 +8,10 @@ and this project adheres to
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- 🐛(aliases) authorize special domain devnull in alias destinations #1029
|
||||
|
||||
## [1.22.1] - 2026-01-21
|
||||
|
||||
- 🔒️(organization) the first user is not admin #776
|
||||
|
||||
@@ -19,7 +19,7 @@ class Migration(migrations.Migration):
|
||||
('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')),
|
||||
('local_part', models.CharField(max_length=100)),
|
||||
('destination', models.EmailField(max_length=254, verbose_name='destination address')),
|
||||
('destination', models.CharField(max_length=254, validators=[django.core.validators.EmailValidator(allowlist=['localhost', 'devnull'])], verbose_name='destination address')),
|
||||
('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='aliases', to='mailbox_manager.maildomain')),
|
||||
],
|
||||
options={
|
||||
|
||||
@@ -9,6 +9,7 @@ from django.conf import settings
|
||||
from django.contrib.auth.base_user import AbstractBaseUser
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core import exceptions, mail, validators
|
||||
from django.core.validators import EmailValidator
|
||||
from django.db import models
|
||||
from django.db.models.functions import Lower
|
||||
from django.template.loader import render_to_string
|
||||
@@ -460,7 +461,15 @@ class Alias(BaseModel):
|
||||
"""Model for aliases."""
|
||||
|
||||
local_part = models.CharField(max_length=100, blank=False)
|
||||
destination = models.EmailField(_("destination address"), null=False, blank=False)
|
||||
destination = models.CharField(
|
||||
_("destination address"),
|
||||
max_length=254,
|
||||
null=False,
|
||||
blank=False,
|
||||
validators=[
|
||||
EmailValidator(allowlist=["localhost", "devnull"]),
|
||||
],
|
||||
)
|
||||
domain = models.ForeignKey(
|
||||
MailDomain,
|
||||
on_delete=models.CASCADE,
|
||||
|
||||
@@ -182,3 +182,33 @@ def test_api_aliases_create__existing_mailbox_ok(dimail_token_ok):
|
||||
)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert models.Alias.objects.exists()
|
||||
|
||||
|
||||
@responses.activate
|
||||
def test_api_aliases_create__devnull_destination_ok(dimail_token_ok):
|
||||
"""Can create alias where destination is devnull@devnull."""
|
||||
access = factories.MailDomainAccessFactory(
|
||||
role="owner", domain=factories.MailDomainEnabledFactory()
|
||||
)
|
||||
|
||||
client = APIClient()
|
||||
client.force_login(access.user)
|
||||
|
||||
responses.post(
|
||||
re.compile(rf".*/domains/{access.domain.name}/aliases/"),
|
||||
json={
|
||||
"username": "spammy-address",
|
||||
"domain": access.domain.name,
|
||||
"destination": "devnull@devnull",
|
||||
"allow_to_send": False,
|
||||
},
|
||||
status=status.HTTP_201_CREATED,
|
||||
content_type="application/json",
|
||||
)
|
||||
|
||||
response = client.post(
|
||||
f"/api/v1.0/mail-domains/{access.domain.slug}/aliases/",
|
||||
{"local_part": "spammy-address", "destination": "devnull@devnull"},
|
||||
)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert models.Alias.objects.exists()
|
||||
|
||||
19
src/backend/mailbox_manager/tests/models/test_aliases.py
Normal file
19
src/backend/mailbox_manager/tests/models/test_aliases.py
Normal file
@@ -0,0 +1,19 @@
|
||||
"""
|
||||
Unit tests for the Alias model
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from mailbox_manager import factories, models
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_models_aliases__devnull_destination_ok():
|
||||
"""Can create alias where destination is devnull@devnull."""
|
||||
|
||||
models.Alias.objects.create(
|
||||
local_part="spam",
|
||||
domain=factories.MailDomainEnabledFactory(),
|
||||
destination="devnull@devnull",
|
||||
)
|
||||
@@ -199,6 +199,12 @@ def test_dimail_synchronization__synchronize_aliases(dimail_token_ok): # pylint
|
||||
"destination": existing_mailbox.secondary_email,
|
||||
"allow_to_send": False,
|
||||
},
|
||||
{ # alias to devnull@devnull
|
||||
"username": "spam",
|
||||
"domain": alias.domain.name,
|
||||
"destination": "devnull@devnull",
|
||||
"allow_to_send": False,
|
||||
},
|
||||
]
|
||||
responses.get(
|
||||
re.compile(rf".*/domains/{alias.domain.name}/aliases/"),
|
||||
@@ -209,8 +215,8 @@ def test_dimail_synchronization__synchronize_aliases(dimail_token_ok): # pylint
|
||||
|
||||
imported_aliases = dimail_client.import_aliases(alias.domain)
|
||||
|
||||
assert len(imported_aliases) == 3
|
||||
assert models.Alias.objects.count() == 4
|
||||
assert len(imported_aliases) == 4
|
||||
assert models.Alias.objects.count() == 5
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
Reference in New Issue
Block a user