Random aliases.

see #522
This commit is contained in:
Antoine Nguyen
2016-07-20 19:19:38 +02:00
parent 019caae72a
commit 66a3404669
8 changed files with 123 additions and 22 deletions
+29 -5
View File
@@ -20,6 +20,7 @@ from ..models import Domain, Alias
class AliasForm(forms.ModelForm, DynamicForm):
"""A form to create/modify an alias."""
random_address = forms.BooleanField(required=False)
address = lib_fields.UTF8AndEmptyUserEmailField(
label=ugettext_lazy("Email address"),
help_text=ugettext_lazy(
@@ -39,16 +40,27 @@ class AliasForm(forms.ModelForm, DynamicForm):
class Meta:
model = Alias
fields = ("address", "enabled", )
fields = ("address", "domain", "enabled", "expire_at", "description")
widgets = {
"domain": forms.widgets.Select(
attrs={"class": "selectpicker", "data-live-search": "true"})
}
def __init__(self, user, *args, **kwargs):
self.user = user
super(AliasForm, self).__init__(*args, **kwargs)
self.fields = OrderedDict(
(key, self.fields[key]) for key in
['address', 'recipients', 'enabled']
["random_address", "address", "domain", "recipients", "enabled",
"expire_at", "description"]
)
if self.instance.pk:
del self.fields["random_address"]
del self.fields["domain"]
else:
self.fields["address"].required = False
self.fields["domain"].required = False
self.fields["domain"].queryset = Domain.objects.get_for_admin(user)
if len(args) and isinstance(args[0], QueryDict):
if "instance" in kwargs:
if not kwargs["instance"].domain.enabled:
@@ -66,6 +78,8 @@ class AliasForm(forms.ModelForm, DynamicForm):
def clean_address(self):
"""Check if address points to a local domain."""
if not self.cleaned_data["address"]:
return self.cleaned_data["address"]
localpart, domname = split_mailbox(self.cleaned_data["address"])
try:
domain = Domain.objects.get(name=domname)
@@ -87,6 +101,12 @@ class AliasForm(forms.ModelForm, DynamicForm):
def clean(self):
"""Check it there is at least one recipient."""
super(AliasForm, self).clean()
random_address = self.cleaned_data.get("random_address")
if not random_address:
if not self.cleaned_data.get("address"):
self.add_error("address", _("This field is required"))
elif not self.cleaned_data.get("domain"):
self.add_error("domain", _("This field is required"))
for field, value in self.cleaned_data.items():
if field.startswith("recipients") and value:
return self.cleaned_data
@@ -96,8 +116,12 @@ class AliasForm(forms.ModelForm, DynamicForm):
def save(self, commit=True):
"""Custom save method."""
alias = super(AliasForm, self).save(commit=False)
local_part, domname = split_mailbox(self.cleaned_data["address"])
alias.domain = Domain.objects.get(name=domname)
if self.cleaned_data.get("random_address"):
alias.address = "{}@{}".format(
Alias.generate_random_address(), alias.domain)
else:
local_part, domname = split_mailbox(self.cleaned_data["address"])
alias.domain = Domain.objects.get(name=domname)
if commit:
alias.save()
address_list = [
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.5 on 2016-07-19 07:03
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('admin', '0004_auto_20160608_1423'),
]
operations = [
migrations.AddField(
model_name='alias',
name='description',
field=models.TextField(blank=True),
),
migrations.AddField(
model_name='alias',
name='expire_at',
field=models.DateTimeField(blank=True, null=True),
),
]
+13
View File
@@ -1,5 +1,8 @@
"""Models related to aliases management."""
import hashlib
import random
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.encoding import python_2_unicode_compatible, smart_text
@@ -37,6 +40,8 @@ class Alias(AdminObject):
default=True
)
internal = models.BooleanField(default=False)
description = models.TextField(blank=True)
expire_at = models.DateTimeField(blank=True, null=True)
_objectname = 'MailboxAlias'
class Meta:
@@ -50,6 +55,14 @@ class Alias(AdminObject):
def __str__(self):
return smart_text(self.address)
@classmethod
def generate_random_address(self):
"""Generate a random address (local part)."""
m = hashlib.md5()
for x in random.sample(xrange(10000000), 60):
m.update(str(x))
return m.hexdigest()[:20]
@property
def identity(self):
return self.address
+3 -3
View File
@@ -52,17 +52,17 @@ class AliasesMap(object):
mysql = (
"SELECT alr.address FROM modoboa_admin_aliasrecipient AS alr "
"INNER JOIN admin_alias AS al ON alr.alias_id=al.id "
"WHERE al.enabled=1 AND al.address='%s'"
"WHERE al.enabled=1 AND al.address='%s' AND al.expire_at>now()"
)
postgres = (
"SELECT alr.address FROM modoboa_admin_aliasrecipient AS alr "
"INNER JOIN admin_alias AS al ON alr.alias_id=al.id "
"WHERE al.enabled AND al.address='%s'"
"WHERE al.enabled AND al.address='%s' AND al.expire_at>now()"
)
sqlite = (
"SELECT alr.address FROM modoboa_admin_aliasrecipient AS alr "
"INNER JOIN admin_alias AS al ON alr.alias_id=al.id "
"WHERE al.enabled=1 AND al.address='%s'"
"WHERE al.enabled=1 AND al.address='%s' AND al.expire_at>now()"
)
+12
View File
@@ -552,6 +552,15 @@ Identities.prototype = {
},
aliasform_cb: function() {
$("#id_random_address").click(function(evt) {
if ($(this).prop("checked")) {
$("#id_address").parents(".form-group").hide();
$("#id_domain").parents(".form-group").show();
} else {
$("#id_address").parents(".form-group").show();
$("#id_domain").parents(".form-group").hide();
}
});
$("#id_address").autocompleter({
from_character: "@",
choices: $.proxy(this.get_domain_list, this)
@@ -565,6 +574,9 @@ Identities.prototype = {
return true;
}
});
$("#id_expire_at").datetimepicker({
format: 'YYYY-MM-DD HH:mm:ss'
});
$(".submit").on('click', $.proxy(function(e) {
simple_ajax_form_post(e, {
formid: "aliasform",
@@ -25,6 +25,12 @@
<dl class="dl-horizontal">
<dt>{% trans "Created" %}</dt><dd>{{ alias.creation|date:"SHORT_DATETIME_FORMAT" }}</dd>
<dt>{% trans "Modified" %}</dt><dd>{{ alias.last_modification|date:"SHORT_DATETIME_FORMAT" }}</dd>
{% if alias.expire_at %}
<dt>{% trans "Expire at" %}</dt><dd>{{ alias.expire_at|date:"SHORT_DATETIME_FORMAT" }}</dd>
{% endif %}
{% if alias.description %}
<dt>{% trans "Description" %}</dt><dd>{{ alias.description }}</dd>
{% endif %}
</dl>
</div>
<table class="table">
+15 -8
View File
@@ -1,10 +1,17 @@
{% extends "common/generic_modal_form.html" %}{% load form_tags %}
{% extends "common/generic_modal_form.html" %}
{% load form_tags %}
{% block formbody %}
{% render_field form.address %}
{% render_fields_group form "recipients" %}
{% if not form.instance.pk or form.instance.domain.enabled %}
{% render_field form.enabled %}
{% else %}
{% include "common/generic_field.html" with field=form.enabled label_width="col-sm-4" inline_message=_("Domain is disabled") extra_classes="info" %}
{% endif %}
{% if form.random_address %}{% render_field form.random_address %}{% endif %}
{% render_field form.address %}
{% if form.domain %}{% render_field form.domain hidden=True %}{% endif %}
{% render_fields_group form "recipients" %}
{% if not form.instance.pk or form.instance.domain.enabled %}
{% render_field form.enabled %}
{% else %}
{% include "common/generic_field.html" with field=form.enabled label_width="col-sm-4" inline_message=_("Domain is disabled") extra_classes="info" %}
{% endif %}
{% render_field form.expire_at %}
{% render_field form.description %}
{% endblock %}
+20 -6
View File
@@ -8,7 +8,7 @@ from modoboa.core.models import User
from modoboa.lib.tests import ModoTestCase
from .. import factories
from ..models import Alias, AliasRecipient
from ..models import Alias, AliasRecipient, Domain
class AliasTestCase(ModoTestCase):
@@ -126,11 +126,13 @@ class AliasTestCase(ModoTestCase):
)
def test_dlist(self):
values = dict(address="all@test.com",
recipients="user@test.com",
recipients_1="admin@test.com",
recipients_2="ext@titi.com",
enabled=True)
values = {
"address": "all@test.com",
"recipients": "user@test.com",
"recipients_1": "admin@test.com",
"recipients_2": "ext@titi.com",
"enabled": True
}
self.ajax_post(
reverse("admin:alias_add"), values
)
@@ -214,6 +216,18 @@ class AliasTestCase(ModoTestCase):
reverse("admin:alias_add"), values
)
def test_random_alias(self):
"""Test creation of a random alias."""
alias_count = Alias.objects.count()
values = {
"random_address": True,
"domain": Domain.objects.get(name="test.com").pk,
"recipients": "user@test.com",
"enabled": True
}
self.ajax_post(reverse("admin:alias_add"), values)
self.assertEqual(Alias.objects.count(), alias_count + 1)
def test_distribution_list_deletion_on_user_update_bug(self):
"""This test demonstrates an issue with distribution list being
deleted when one of the users which belong to that list is