use honeypot field to detect robots on registration form (#50108)

This commit is contained in:
Benjamin Dauvergne 2021-01-14 10:30:46 +01:00
parent ab66385315
commit 961403a666
6 changed files with 60 additions and 3 deletions

View File

@ -0,0 +1,33 @@
# authentic2 - versatile identity manager
# Copyright (C) 2010-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.core.exceptions import ValidationError
from django.forms import Form, CheckboxInput, BooleanField
from django.utils.html import mark_safe
from django.utils.translation import gettext as _
class HoneypotInput(CheckboxInput):
template_name = 'authentic2/honeypot_input.html'
is_hidden = True
class HoneypotForm(Form):
robotcheck = BooleanField(widget=HoneypotInput, required=False)
def is_robot(self):
return hasattr(self, 'cleaned_data') and self.cleaned_data.get('robotcheck')

View File

@ -19,7 +19,6 @@ import re
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _, ugettext
from django.forms import Form
from django.contrib.auth.models import BaseUserManager, Group
@ -28,12 +27,13 @@ from authentic2.a2_rbac.models import OrganizationalUnit
from .. import app_settings, models
from . import profile as profile_forms
from .honeypot import HoneypotForm
from .fields import ValidatedEmailField
User = get_user_model()
class RegistrationForm(Form):
class RegistrationForm(HoneypotForm):
error_css_class = 'form-field-error'
required_css_class = 'form-field-required'

View File

@ -0,0 +1 @@
{% load i18n %}<label style="display: none"><input type="{{ widget.type }}" name="{{ widget.name }}"><span>{% trans "Robot detection, do not check !" %}</span></label>

View File

@ -6,8 +6,10 @@
{% endblock %}
{% block content %}
{% if 'robot' in request.GET %}
<p>{% blocktrans %}<strong>Your registration request has been refused.</strong> Indeed your browser checked a hidden anti-robot checkbox on the registration form. A browser extension may produce this behaviour, in this case disable such extensions and try again.{% endblocktrans %}</p>
{% else %}
{% block instructions %}
<p><strong>
{% blocktrans with email=request.session.registered_email %}
An email was sent to {{ email }}.
{% endblocktrans %}
@ -33,6 +35,7 @@
{% endblocktrans %}
</p>
{% endblock %}
{% endif %}
{% block back %}
<p><a href="{{ next_url }}">{% trans "Back" %}</a></p>
{% endblock %}

View File

@ -829,6 +829,12 @@ class BaseRegistrationView(FormView):
return super(BaseRegistrationView, self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
if form.is_robot():
return utils.redirect(self.request, 'registration_complete',
params={
REDIRECT_FIELD_NAME: self.next_url,
'robot': 'on',
})
email = form.cleaned_data.pop('email')
# if an email has already been sent, warn once before allowing resend

View File

@ -819,3 +819,17 @@ def test_registration_email_not_verified_required_and_unrequired_attributes(app,
assert user.attributes.preferred_color == 'bleu'
assert user.email == 'john.doe2@example.com'
assert user.email_verified is True
def test_honeypot(app, db, settings, mailoutbox):
settings.DEFAULT_FROM_EMAIL = 'show only addr <noreply@example.net>'
response = app.get(utils.make_url('registration_register'))
response = app.post(utils.make_url('registration_register'), params={
'email': 'testbot@entrouvert.com',
'csrfmiddlewaretoken': response.context['csrf_token'],
'robotcheck': 'a',
})
response = response.follow()
assert len(mailoutbox) == 0
assert 'Your registration request has been refused' in response