From 4f62f6eff2bea445d8b9488759fe343db1a1153b Mon Sep 17 00:00:00 2001 From: Kumi Date: Sun, 23 Jun 2024 16:47:07 +0200 Subject: [PATCH] feat: rate limiting and Redis caching setup Added rate limiting to the SendLoginEmailView to prevent abuse by applying the `ratelimit` decorator. Configured Redis as a cache backend based on settings from the configuration file. Updated dependencies to include `django-ratelimit`, `redis`, and `django-redis` packages to support these enhancements. These changes improve the security and performance of the application by limiting login email attempts and using Redis for caching. --- freedoi/accounts/views.py | 4 ++++ freedoi/settings.py | 14 ++++++++++++++ pyproject.toml | 3 +++ 3 files changed, 21 insertions(+) diff --git a/freedoi/accounts/views.py b/freedoi/accounts/views.py index 98ecdca..e0c95b1 100644 --- a/freedoi/accounts/views.py +++ b/freedoi/accounts/views.py @@ -8,6 +8,7 @@ from django.views.generic import FormView, View from django.contrib.auth.tokens import default_token_generator from django.contrib.auth import logout from django.contrib.auth.mixins import LoginRequiredMixin +from django.utils.decorators import method_decorator from rest_framework.authtoken.models import Token from rest_framework.authtoken.views import ObtainAuthToken @@ -15,11 +16,14 @@ from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.permissions import IsAuthenticated +from django_ratelimit.decorators import ratelimit + from .forms import EmailForm, GenerateTokenForm User = get_user_model() +@method_decorator(ratelimit(key="ip", rate="1/m", method="POST"), name="dispatch") class SendLoginEmailView(FormView): template_name = "accounts/send_login_email.html" form_class = EmailForm diff --git a/freedoi/settings.py b/freedoi/settings.py index ab2376f..434d734 100644 --- a/freedoi/settings.py +++ b/freedoi/settings.py @@ -188,3 +188,17 @@ else: EMAIL_SUBJECT_PREFIX = "[FreeDOI] " DEFAULT_FROM_EMAIL = "noreply@freedoi.org" + +# Redis + +if (redis_host := CONFIG.get("Redis", "Host", fallback=None)) is not None: + CACHES = { + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": f"redis://{redis_host}:{CONFIG.getint('Redis', 'Port', fallback=6379)}/1", + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + }, + } + } + diff --git a/pyproject.toml b/pyproject.toml index 6b9fd06..4e36bfd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,9 @@ django-crispy-forms = "*" crispy-bootstrap5 = "*" django-two-factor-auth = "*" phonenumbers = "*" +django-ratelimit = "*" +redis = "*" +django-redis = "*" [tool.poetry.group.mysql.dependencies] mysqlclient = "*"