summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorMagnus Hagander2019-06-19 19:14:19 +0000
committerMagnus Hagander2019-06-19 19:14:19 +0000
commit6394cfb1c2ad1e69f5904829898197e89b0d1cf4 (patch)
tree7d7b85bf7c7d77e12545d530aac4172725ca30d3 /django
parent311529269a52c6d24a3da8bdc3cea7d9e80284f3 (diff)
Re-think rate limiting for resending
The way it was done ended up defeaeting the service sending things right away for people who did *not* violate the rate limit. So instead, keep track of exactly when the last email was sent for each user, and rate-limit based on that.
Diffstat (limited to 'django')
-rw-r--r--django/archives/mailarchives/migrations/0004_resend_rate_limit.py30
-rw-r--r--django/archives/mailarchives/models.py8
-rw-r--r--django/archives/mailarchives/views.py9
3 files changed, 45 insertions, 2 deletions
diff --git a/django/archives/mailarchives/migrations/0004_resend_rate_limit.py b/django/archives/mailarchives/migrations/0004_resend_rate_limit.py
new file mode 100644
index 0000000..bdd522f
--- /dev/null
+++ b/django/archives/mailarchives/migrations/0004_resend_rate_limit.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.18 on 2019-06-19 19:02
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('auth', '0008_alter_user_username_max_length'),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('mailarchives', '0003_message_resend'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='LastResentMessage',
+ fields=[
+ ('sentto', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)),
+ ('sentat', models.DateTimeField()),
+ ],
+ ),
+ migrations.AlterUniqueTogether(
+ name='resendmessage',
+ unique_together=set([('message', 'sendto')]),
+ ),
+ ]
diff --git a/django/archives/mailarchives/models.py b/django/archives/mailarchives/models.py
index 5c0c1e0..0affa3b 100644
--- a/django/archives/mailarchives/models.py
+++ b/django/archives/mailarchives/models.py
@@ -126,6 +126,14 @@ class ResendMessage(models.Model):
sendto = models.ForeignKey(User, null=False, blank=False)
registeredat = models.DateTimeField(null=False, blank=False)
+ class Meta:
+ unique_together = (('message', 'sendto'), )
+
+
+class LastResentMessage(models.Model):
+ sentto = models.ForeignKey(User, null=False, blank=False, primary_key=True)
+ sentat = models.DateTimeField(null=False, blank=False)
+
class ApiClient(models.Model):
apikey = models.CharField(max_length=100, null=False, blank=False)
diff --git a/django/archives/mailarchives/views.py b/django/archives/mailarchives/views.py
index 03fdf7b..4835e7e 100644
--- a/django/archives/mailarchives/views.py
+++ b/django/archives/mailarchives/views.py
@@ -647,10 +647,15 @@ def resend(request, messageid):
if request.method == 'POST':
if request.POST.get('resend', None) == '1':
# 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():
+ if LastResentMessage.objects.filter(sentto=request.user, sentat__gt=datetime.now() - timedelta(seconds=settings.RESEND_RATE_LIMIT_SECONDS)).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))
+ ResendMessage.objects.get_or_create(message=m, sendto=request.user, defaults={
+ 'registeredat': datetime.now(),
+ })
+ connection.cursor().execute("INSERT INTO mailarchives_lastresentmessage (sentto_id, sentat) VALUES (%(id)s, CURRENT_TIMESTAMP) ON CONFLICT (sentto_id) DO UPDATE SET sentat=EXCLUDED.sentat", {
+ 'id': request.user.id,
+ })
connection.cursor().execute("NOTIFY archives_resend")
return HttpResponseRedirect('/message-id/resend/{0}/complete'.format(m.messageid))