Merge pull request #915 from bearstech/mx_ui

improve mx UI; move valid-mx parameters to UI too
This commit is contained in:
Antoine Nguyen
2016-09-16 10:31:20 +02:00
committed by GitHub
6 changed files with 68 additions and 26 deletions
+11
View File
@@ -61,6 +61,17 @@ class AdminParametersForm(parameters.AdminParametersForm):
)
)
valid_mxs = forms.CharField(
label=ugettext_lazy("Valid MXs"),
initial="",
help_text=ugettext_lazy(
"IP(s) or network(s) "
"of the MX that should match with domains' MX records. "
"On per line."
),
widget=forms.Textarea
)
enable_dnsbl_checks = YesNoField(
label=ugettext_lazy("Enable DNSBL checks"),
initial="yes",
@@ -38,12 +38,15 @@ class CheckMXRecords(BaseCommand):
"""Return sender address for notifications."""
return parameters.get_admin("SENDER_ADDRESS", app="core")
@cached_property
def valid_mxs(self):
"""Return valid MXs set in admin."""
valid_mxs = parameters.get_admin("VALID_MXS", app="admin")
return [ipaddress.ip_network(u"{}".format(v.strip()))
for v in valid_mxs.split() if v.strip()]
def add_arguments(self, parser):
"""Add extra arguments to command."""
parser.add_argument(
"--valid-mx", type=str, action="append", default=[],
help="Valid MX ip(s) or subnet(s) used to check that domain's MX "
"are your own.")
parser.add_argument(
"--no-dnsbl", action="store_true", default=False,
help="Skip DNSBL queries.")
@@ -158,7 +161,7 @@ class CheckMXRecords(BaseCommand):
if not status:
print(msg)
def check_valid_mx(self, domain, mx_list, valid_mx=None, **options):
def check_valid_mx(self, domain, mx_list, **options):
"""Check that domain's MX record exist.
If `valid_mx` is provided, retrieved MX records must be
@@ -166,14 +169,17 @@ class CheckMXRecords(BaseCommand):
"""
alerts = []
check = False
mxs = [ipaddress.ip_address(u"%s" % mx.address) for mx in mx_list]
mxs = [(mx, ipaddress.ip_address(u"%s" % mx.address))
for mx in mx_list]
valid_mxs = self.valid_mxs
if not mxs:
alerts.append(_("Domain {} as no MX record").format(domain))
elif valid_mx:
for subnet in valid_mx:
check = True in [mx in subnet for mx in mxs]
if check is True:
break
elif valid_mxs:
for subnet in valid_mxs:
for mx, addr in mxs:
if addr in subnet:
mx.managed = check = True
mx.save()
if check is False:
mx_names = [
"{0.name} ({0.address})".format(mx) for mx in mx_list]
@@ -192,7 +198,7 @@ class CheckMXRecords(BaseCommand):
return
content = render_to_string(
"admin/notifications/domain_invalid_mx.html", {
"domain": domain, "valid_mx": valid_mx, "alerts": alerts
"domain": domain, "alerts": alerts
})
subject = _("[modoboa] MX issue(s) for domain {}").format(
domain.name)
@@ -227,11 +233,6 @@ class CheckMXRecords(BaseCommand):
def handle(self, *args, **options):
"""Command entry point."""
# Check that user provide valid network addresses
valid_mx = options["valid_mx"]
options["valid_mx"] = [
ipaddress.ip_network(u"{}".format(v)) for v in valid_mx]
# Remove deprecated records first
models.DNSBLResult.objects.exclude(
provider__in=self.providers).delete()
+18
View File
@@ -224,6 +224,17 @@ class Domain(AdminObject):
reversion.register(Domain)
class MXQuerySet(models.QuerySet):
"""Custom manager for MXRecord."""
def has_valids(self):
"""Return managed results."""
if parameters.get_admin("ENABLE_MX_CHECKS"):
if parameters.get_admin("VALID_MXS", app="admin").strip():
return self.filter(managed=True).exists()
return self.exists()
class MXRecord(models.Model):
"""A model used to store MX records for Domain."""
@@ -233,6 +244,13 @@ class MXRecord(models.Model):
managed = models.BooleanField(default=False)
updated = models.DateTimeField()
objects = models.Manager.from_queryset(MXQuerySet)()
def is_managed(self):
if not parameters.get_admin("ENABLE_MX_CHECKS"):
return False
return bool(parameters.get_admin("VALID_MXS", app="admin").strip())
def __unicode__(self):
return u'{0.name} ({0.address}) for {0.domain} '.format(self)
@@ -23,7 +23,7 @@
<div class="panel-body">
<div class="row">
<span class="title">{% trans "DNS status" %}</span>
<span class="label label-mini label-{% if not domain.mxrecord_set.exists %}danger{% else %}success{% endif %}"><a href="{% url 'admin:mx_domain_detail' domain.pk %}" data-toggle="ajaxmodal">MX</a></span>
<span class="label label-mini label-{% if not domain.mxrecord_set.has_valids %}danger{% else %}success{% endif %}"><a href="{% url 'admin:mx_domain_detail' domain.pk %}" data-toggle="ajaxmodal">MX</a></span>
<span class="label label-{{ domain.dnsbl_status_color }}"><a href="{% url 'admin:dnsbl_domain_detail' domain.pk %}" data-toggle="ajaxmodal">DNSBL</a></span>
</div>
<div class="row">
@@ -15,14 +15,27 @@
{% for mx in domain.mxrecord_set.all %}
<tr>
<td>{{ mx.name }}</td>
<td>{{ mx.address }}</td>
<td>
{% if mx.is_managed %}
{% if not mx.managed %}
<a class="label label-danger help"
rel="popover"
data-content="{% trans 'This IP does not match any of the valid MXs defined in parameters' %}"
>{{ mx.address }}</a>
{% else %}
<span class="label label-success">{{ mx.address }}</span>
{% endif %}
{% else %}
{{ mx.address }}
{% endif %}
</td>
<td>{{ mx.updated|date:"SHORT_DATETIME_FORMAT" }}</td>
</tr>
{% empty %}
<tr>
<td colspan="3">{% trans "No MX record found for this domain." %}</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
{% endblock %}
+5 -6
View File
@@ -5,6 +5,7 @@ from django.core.urlresolvers import reverse
from django.test import override_settings
from modoboa.lib.tests import ModoTestCase
from modoboa.lib import parameters
from .. import factories
from .. import models
@@ -19,28 +20,26 @@ class MXTestCase(ModoTestCase):
super(MXTestCase, cls).setUpTestData()
cls.domain = factories.DomainFactory(name="modoboa.org")
factories.DomainFactory(name="pouet.com") # should not exist
parameters.save_admin('VALID_MXS', '127.0.0.1', app='admin')
def test_management_command(self):
"""Check that command works fine."""
self.assertEqual(models.MXRecord.objects.count(), 0)
management.call_command("modo", "check_mx", "--no-dnsbl",
"--valid-mx=127.0.0.1", "--ttl=0")
management.call_command("modo", "check_mx", "--no-dnsbl", "--ttl=0")
self.assertTrue(
models.MXRecord.objects.filter(domain=self.domain).exists())
# we passed a ttl to 0. this will reset the cache this time
qs = models.MXRecord.objects.filter(domain=self.domain)
id = qs[0].id
management.call_command("modo", "check_mx", "--no-dnsbl",
"--valid-mx=127.0.0.1", "--ttl=7200")
management.call_command("modo", "check_mx", "--no-dnsbl", "--ttl=7200")
qs = models.MXRecord.objects.filter(domain=self.domain)
self.assertNotEqual(id, qs[0].id)
id = qs[0].id
# assume that mxrecords ids are the same. means that we taking care of
# ttl
management.call_command("modo", "check_mx", "--no-dnsbl",
"--valid-mx=127.0.0.1")
management.call_command("modo", "check_mx", "--no-dnsbl")
qs = models.MXRecord.objects.filter(domain=self.domain)
self.assertEqual(id, qs[0].id)