diff --git a/src/plainui/forms.py b/src/plainui/forms.py index f63b56dbbaf4765e1cc7fd8badda0da509d3e195..591c558336d344c253d99a1a9fe51f94cb18ae0d 100644 --- a/src/plainui/forms.py +++ b/src/plainui/forms.py @@ -265,7 +265,7 @@ class ReportForm(forms.Form): if kind == 'url': reported_content = self.cleaned_data['kind_data'] elif kind == 'pn': - dm = DirectMessage.objects.get(pk=self.cleaned_data['kind_data'], recipient=request.user) + dm = DirectMessage.objects.get(pk=self.cleaned_data['kind_data']) reported_content = 'DM by %(sender)s (%(sender_id)s) to %(recipient)s (%(recipient_id)s) at %(timestamp)s (%(uuid)s).\n' reported_content += 'Subject: "%(subject)s"\nMessage: "%(body)s"' reported_content %= { diff --git a/src/plainui/tests.py b/src/plainui/tests.py index cd59057e9fb4a97edcf8b56550b8418341eb58d0..0dc5e996413bd94e9a8a2bff61d9bed77a361778 100644 --- a/src/plainui/tests.py +++ b/src/plainui/tests.py @@ -1021,7 +1021,7 @@ class ViewsTest(TestCase): self.assertEqual(resp.context_data['conf'], self.conf) self.assertEqual(resp.context_data['form']['recipient'].value(), 'blgl') - @override_settings(LANGUAGE_CODE='en') + @override_settings(LANGUAGE_CODE='en', AUTOBAN_KEYWORDS=['blork', re.compile('12.45')]) def test_PersonalMessageSendView_post(self): user2 = PlatformUser(username='testuser2') user2.save() @@ -1073,6 +1073,38 @@ class ViewsTest(TestCase): self.assertEqual(form.errors['subject'], ['This field is required.']) self.assertEqual(form.errors['body'], ['This field is required.']) + DirectMessage.objects.all().delete() + self.user.refresh_from_db() + self.assertFalse(self.user.shadow_banned) + self.assertEqual(len(mail.outbox), 0) + resp = self.client.post(reverse('plainui:personal_message_send', kwargs={'conf_slug': self.conf.slug}), { + 'in_reply_to': '', + 'recipient': 'testuser2', + 'subject': 'blork', + 'body': 'Body2', + }) + self.assertRedirects(resp, reverse('plainui:personal_message', kwargs={'conf_slug': self.conf.slug})) + new_dm = DirectMessage.objects.get() + self.user.refresh_from_db() + self.assertTrue(self.user.shadow_banned) + self.assertTrue(new_dm.deleted_by_recipient, True) + self.assertEqual(len(mail.outbox), 1) + + self.user.shadow_banned = False + self.user.save() + resp = self.client.post(reverse('plainui:personal_message_send', kwargs={'conf_slug': self.conf.slug}), { + 'in_reply_to': '', + 'recipient': 'testuser2', + 'subject': 'this is ok', + 'body': '12345', + }) + self.assertRedirects(resp, reverse('plainui:personal_message', kwargs={'conf_slug': self.conf.slug})) + new_dm = DirectMessage.objects.get(subject='this is ok') + self.user.refresh_from_db() + self.assertTrue(self.user.shadow_banned) + self.assertTrue(new_dm.deleted_by_recipient, True) + self.assertEqual(len(mail.outbox), 2) + def test_PersonalMessageShowView(self): user2 = PlatformUser(username='testuser2') user2.save() @@ -1345,7 +1377,7 @@ class ViewsTest(TestCase): self.assertEqual(resp.context_data['form']['title'].value(), '') self.assertEqual(resp.context_data['form']['text'].value(), '') - @override_settings(LANGUAGE_CODE='en') + @override_settings(LANGUAGE_CODE='en', AUTOBAN_KEYWORDS=['foo 123', re.compile('asdf.foo')]) def test_BoardEntryEditView_post(self): user2 = PlatformUser(username='testuser2') user2.save() @@ -1400,6 +1432,33 @@ class ViewsTest(TestCase): self.assertEqual(new_entry.text, 'some texty text') self.assertSetsMessage(resp, 'Bulletin Board Entry created.') + BulletinBoardEntry.objects.all().delete() + self.user.refresh_from_db() + self.assertFalse(self.user.shadow_banned) + self.assertEqual(len(mail.outbox), 0) + self.assertNeedsLogin(reverse('plainui:board_entry_new', kwargs={'conf_slug': self.conf.slug}), post=True) + resp = self.client.post(reverse('plainui:board_entry_new', kwargs={'conf_slug': self.conf.slug}), { + 'title': 'New Entry', + 'text': 'foo 123', + }) + new_entry = BulletinBoardEntry.objects.get() + self.assertRedirects(resp, reverse('plainui:board_entry_edit', kwargs={'conf_slug': self.conf.slug, 'id': str(new_entry.pk)})) + self.user.refresh_from_db() + self.assertTrue(self.user.shadow_banned) + self.assertEqual(len(mail.outbox), 1) + + self.user.shadow_banned = False + self.user.save() + self.assertNeedsLogin(reverse('plainui:board_entry_edit', kwargs={'conf_slug': self.conf.slug, 'id': str(new_entry.pk)}), post=True) + resp = self.client.post(reverse('plainui:board_entry_edit', kwargs={'conf_slug': self.conf.slug, 'id': str(new_entry.pk)}), { + 'title': 'asdf4foo', + 'text': 'blblblblbl', + }) + self.assertRedirects(resp, reverse('plainui:board_entry_edit', kwargs={'conf_slug': self.conf.slug, 'id': str(new_entry.pk)})) + self.user.refresh_from_db() + self.assertTrue(self.user.shadow_banned) + self.assertEqual(len(mail.outbox), 2) + def test_BoardEntryDeleteView_get(self): self.assertNeedsLogin(reverse('plainui:board_entry_delete', kwargs={'conf_slug': self.conf.slug})) resp = self.client.get(reverse('plainui:board_entry_delete', kwargs={'conf_slug': self.conf.slug})) diff --git a/src/plainui/utils.py b/src/plainui/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..c8455d1d8be8ba7cdae54fb1303ad51df3cff64b --- /dev/null +++ b/src/plainui/utils.py @@ -0,0 +1,35 @@ +import re + +from django.conf import settings + +from .forms import ReportForm + + +def check_message_content(conf, request, text, kind, kind_data): + try: + trigger_autoban = False + for pattern in settings.AUTOBAN_KEYWORDS: + if isinstance(pattern, re.Pattern): + trigger_autoban |= pattern.search(text) is not None + elif pattern in text: + trigger_autoban = True + + if not trigger_autoban: + return False + + request.user.shadow_banned = True + request.user.save(update_fields=['shadow_banned']) + + report_form = ReportForm(conf=conf, data={ + 'kind': kind, + 'kind_data': str(kind_data), + 'category': 'abuse', + 'message': 'Autoban triggered', + 'message2': '.', + }) + report_form.is_valid() + + report_form.send_report_mail('plainui/registration/report_mail_body.txt', request) + return True + except Exception: + raise Exception("Boom") # don't allow leaking eg. ValidationErrors that might be handled in a View diff --git a/src/plainui/views.py b/src/plainui/views.py index 0c0a6922ce1336f086683d480ffbaabcd5b496a9..2ee289bea7535b57ac18128ded3422b0ce1ae4b6 100644 --- a/src/plainui/views.py +++ b/src/plainui/views.py @@ -36,6 +36,7 @@ from .forms import BulletinBoardEntryForm, ExampleForm, InputTokenForm, NewDirec ProfileEditForm, SelfOrganizedSessionForm, RedeemTokenAddToUserForm, RedeemTokenUserCreateForm, ReportForm, \ RedeemBadgeForm, TokenPasswortResetForm from .models import BulletinBoardEntry +from .utils import check_message_content def _session_refresh_favorite_assemblies(session, user) -> List[str]: @@ -852,6 +853,10 @@ class PersonalMessageSendView(ConferenceRequiredMixin, FormView): if in_reply_to: DirectMessage.objects.filter(id=in_reply_to, recipient=self.request.user).update(has_responded=True) + if check_message_content(self.conf, self.request, form.cleaned_data['subject'], 'pn', dm.pk) or \ + check_message_content(self.conf, self.request, form.cleaned_data['body'], 'pn', dm.pk): + dm.deleted_by_recipient = True + dm.save(update_fields=['deleted_by_recipient']) messages.success(self.request, gettext("Message sent.")) return super().form_valid(form) @@ -1106,6 +1111,9 @@ class BoardEntryEditView(ConferenceRequiredMixin, FormView): self.board_entry.text = form.cleaned_data['text'] self.board_entry.save() + check_message_content(self.conf, self.request, form.cleaned_data['title'], 'board', self.board_entry.pk) + check_message_content(self.conf, self.request, form.cleaned_data['text'], 'board', self.board_entry.pk) + if 'id' in self.kwargs: messages.success(self.request, gettext("Bulletin Board Entry updated.")) else: diff --git a/src/rc3platform/settings/base.py b/src/rc3platform/settings/base.py index 85f85aab470813be34c554a51ee056d435da4ac8..4551ff24a9806cfed2e4ad261d8f31546c25782e 100644 --- a/src/rc3platform/settings/base.py +++ b/src/rc3platform/settings/base.py @@ -189,6 +189,9 @@ REST_FRAMEWORK = { # List of disallowed assembly slugs FORBIDDEN_ASSEMBLY_SLUGS = ['visit', 'maps', 'api', 'pusher'] +# list of autoban keywords. (Usage will trigger a report and send an abuse mail). Use strings or re.compile +AUTOBAN_KEYWORDS = [] + # Mail configuration MAIL_REPLY_TO = [] SUPPORT_HTML_MAILS = False