Implement basic rate limiting for sending email
authorMagnus Hagander <magnus@hagander.net>
Tue, 18 Jun 2019 20:12:45 +0000 (22:12 +0200)
committerMagnus Hagander <magnus@hagander.net>
Tue, 18 Jun 2019 20:19:43 +0000 (22:19 +0200)
Per-user limit that says how many seconds must go between each email.
Outgoing emails are also delayed by this much (or we'd miss it)

django/archives/mailarchives/migrations/0003_message_resend.py
django/archives/mailarchives/models.py
django/archives/mailarchives/views.py
django/archives/settings.py

index 546150288c935227c9cb12b38f4ef71113191af2..c1e0419df7087bc61212fb4654632d8f4ccbb276 100644 (file)
@@ -19,7 +19,7 @@ class Migration(migrations.Migration):
             name='ResendMessage',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('registeredat', models.DateTimeField(auto_now_add=True)),
+                ('registeredat', models.DateTimeField()),
                 ('message', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mailarchives.Message')),
                 ('sendto', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
             ],
index a9ca52e9d9b40bc61cdb6f685952a691e8e411b5..5c0c1e03244f4be27e977d76b730536988c7b7ab 100644 (file)
@@ -124,7 +124,7 @@ class ListSubscriber(models.Model):
 class ResendMessage(models.Model):
     message = models.ForeignKey(Message, null=False, blank=False)
     sendto = models.ForeignKey(User, null=False, blank=False)
-    registeredat = models.DateTimeField(null=False, blank=False, auto_now_add=True)
+    registeredat = models.DateTimeField(null=False, blank=False)
 
 
 class ApiClient(models.Model):
index bd7b8915f0705926470c09fd396b6a08850d75a8..03fdf7b70990491982a88ca4441bd3c3ab684a42 100644 (file)
@@ -646,7 +646,11 @@ def resend(request, messageid):
 
     if request.method == 'POST':
         if request.POST.get('resend', None) == '1':
-            ResendMessage.objects.get_or_create(message=m, sendto=request.user)
+            # Figure out if this user has sent an email recently, and if so refuse it
+            if ResendMessage.objects.filter(sendto=request.user, registeredat__gt=datetime.now()).exists():
+                return HttpResponse("You have already requested an email to be sent in the past {0} seconds. Please try again later.".format(settings.RESEND_RATE_LIMIT_SECONDS))
+
+            ResendMessage.objects.get_or_create(message=m, sendto=request.user, registeredat=datetime.now() + timedelta(seconds=settings.RESEND_RATE_LIMIT_SECONDS))
             connection.cursor().execute("NOTIFY archives_resend")
             return HttpResponseRedirect('/message-id/resend/{0}/complete'.format(m.messageid))
 
index d2918c120bbe61820e26488190b3c52da452422b..c6413fed74307565707af7121cf38d5e7a2dc5e7 100644 (file)
@@ -134,6 +134,9 @@ CSRF_COOKIE_SECURE = True
 # Required for lighttpd
 FORCE_SCRIPT_NAME = ""
 
+# Minimum this many seconds between emails sent out to a single user
+RESEND_RATE_LIMIT_SECONDS = 30
+
 # Always override!
 SEARCH_CLIENTS = ('127.0.0.1',)
 API_CLIENTS = ('127.0.0.1',)