From 2c8f18d7fc47b9eeac9560ed1c7460ac7ab6ce1a Mon Sep 17 00:00:00 2001 From: Sli Date: Mon, 22 Jul 2024 11:40:11 +0200 Subject: [PATCH] Add honeypot on forum --- forum/templates/forum/reply.jinja | 1 + forum/tests.py | 21 +++++++++++++++++++-- forum/views.py | 12 ++++++++++++ sith/settings.py | 1 + 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/forum/templates/forum/reply.jinja b/forum/templates/forum/reply.jinja index b8ab53e5..e4b5f5f0 100644 --- a/forum/templates/forum/reply.jinja +++ b/forum/templates/forum/reply.jinja @@ -29,6 +29,7 @@ {% endif %}
{% csrf_token %} + {% render_honeypot_field settings.HONEYPOT_FIELD_NAME_FORUM %} {{ form.as_p() }}

diff --git a/forum/tests.py b/forum/tests.py index a8d3b583..14c6ca56 100644 --- a/forum/tests.py +++ b/forum/tests.py @@ -14,6 +14,7 @@ # import pytest +from django.conf import settings from django.test import Client from django.urls import reverse from pytest_django.asserts import assertRedirects @@ -24,13 +25,14 @@ from forum.models import Forum, ForumMessage, ForumTopic @pytest.mark.django_db class TestTopicCreation: - def test_topic_creation_success(self, client: Client): + def test_topic_creation_ok(self, client: Client): user: User = User.objects.get(username="root") forum = Forum.objects.get(name="AE") client.force_login(user) payload = { "title": "Hello IT.", "message": "Have you tried turning it off and on again ?", + settings.HONEYPOT_FIELD_NAME_FORUM: settings.HONEYPOT_VALUE, } assert not ForumTopic.objects.filter(_title=payload["title"]).exists() response = client.post(reverse("forum:new_topic", args=str(forum.id)), payload) @@ -46,13 +48,28 @@ class TestTopicCreation: assert topic assert topic.last_message.message == payload["message"] - def test_topic_creation_failure(self, client: Client): + def test_topic_creation_honeypot_fail(self, client: Client): + user: User = User.objects.get(username="root") + forum = Forum.objects.get(name="AE") + client.force_login(user) + payload = { + "title": "You shall", + "message": "Not pass !", + settings.HONEYPOT_FIELD_NAME_FORUM: settings.HONEYPOT_VALUE + "random", + } + assert not ForumTopic.objects.filter(_title=payload["title"]).exists() + response = client.post(reverse("forum:new_topic", args=str(forum.id)), payload) + assert response.status_code == 200 + assert not ForumTopic.objects.filter(_title=payload["title"]).exists() + + def test_topic_creation_fail(self, client: Client): user: User = User.objects.get(username="krophil") forum = Forum.objects.get(name="AE") client.force_login(user) payload = { "title": "You shall", "message": "Not pass !", + settings.HONEYPOT_FIELD_NAME_FORUM: settings.HONEYPOT_VALUE, } assert not ForumTopic.objects.filter(_title=payload["title"]).exists() response = client.post(reverse("forum:new_topic", args=str(forum.id)), payload) diff --git a/forum/views.py b/forum/views.py index 93a19c49..8c9a8900 100644 --- a/forum/views.py +++ b/forum/views.py @@ -22,6 +22,7 @@ # # import math +from functools import partial from ajax_select import make_ajax_field from django import forms @@ -32,11 +33,13 @@ from django.db import IntegrityError from django.shortcuts import get_object_or_404 from django.urls import reverse_lazy from django.utils import html, timezone +from django.utils.decorators import method_decorator from django.utils.translation import gettext_lazy as _ from django.views.generic import DetailView, ListView, RedirectView from django.views.generic.detail import SingleObjectMixin from django.views.generic.edit import CreateView, DeleteView, UpdateView from haystack.query import RelatedSearchQuerySet +from honeypot.decorators import check_honeypot from core.views import ( CanCreateMixin, @@ -242,6 +245,9 @@ class TopicForm(forms.ModelForm): title = forms.CharField(required=True, label=_("Title")) +@method_decorator( + partial(check_honeypot, field_name=settings.HONEYPOT_FIELD_NAME_FORUM), name="post" +) class ForumTopicCreateView(CanCreateMixin, CreateView): model = ForumMessage form_class = TopicForm @@ -331,6 +337,9 @@ class ForumMessageView(SingleObjectMixin, RedirectView): return self.object.get_url() +@method_decorator( + partial(check_honeypot, field_name=settings.HONEYPOT_FIELD_NAME_FORUM), name="post" +) class ForumMessageEditView(CanEditMixin, UpdateView): model = ForumMessage form_class = forms.modelform_factory( @@ -381,6 +390,9 @@ class ForumMessageUndeleteView(SingleObjectMixin, RedirectView): return self.object.get_absolute_url() +@method_decorator( + partial(check_honeypot, field_name=settings.HONEYPOT_FIELD_NAME_FORUM), name="post" +) class ForumMessageCreateView(CanCreateMixin, CreateView): model = ForumMessage form_class = forms.modelform_factory( diff --git a/sith/settings.py b/sith/settings.py index 6424d039..e86076a3 100644 --- a/sith/settings.py +++ b/sith/settings.py @@ -287,6 +287,7 @@ REST_FRAMEWORK["UNAUTHENTICATED_USER"] = "core.models.AnonymousUser" HONEYPOT_FIELD_NAME = "body2" HONEYPOT_VALUE = "content" HONEYPOT_RESPONDER = custom_honeypot_error # Make honeypot errors less suspicious +HONEYPOT_FIELD_NAME_FORUM = "message2" # Only used on forum # Email