diff --git a/docs/configuration.rst b/docs/configuration.rst index d29ef9f1..140a12e6 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -155,6 +155,11 @@ Note: this setting is actually interpreted by Flask-Babel, see the .. _Flask-Babel guide for formatting dates: https://pythonhosted.org/Flask-Babel/#formatting-dates +`ENABLE_CAPTCHA` +--------------- + +It is possible to add a simple captcha in order to filter out spammer bots on the form creation. +In order to do so, you just have to set `ENABLE_CAPTCHA = True`. Configuring emails sending -------------------------- diff --git a/ihatemoney/conf-templates/ihatemoney.cfg.j2 b/ihatemoney/conf-templates/ihatemoney.cfg.j2 index 13a8e9f5..9d117c18 100644 --- a/ihatemoney/conf-templates/ihatemoney.cfg.j2 +++ b/ihatemoney/conf-templates/ihatemoney.cfg.j2 @@ -42,3 +42,7 @@ ACTIVATE_ADMIN_DASHBOARD = False # Enable secure cookies. Requires HTTPS. Disable if you run your ihatemoney # service over plain HTTP. SESSION_COOKIE_SECURE = True + +# You can activate an optional CAPTCHA if you want to. It can be helpful +# to filter spammer bots. +# ENABLE_CAPTCHA = True diff --git a/ihatemoney/default_settings.py b/ihatemoney/default_settings.py index 96795a01..3204ed3b 100644 --- a/ihatemoney/default_settings.py +++ b/ihatemoney/default_settings.py @@ -32,3 +32,4 @@ SUPPORTED_LANGUAGES = [ "uk", "zh_Hans", ] +ENABLE_CAPTCHA = False diff --git a/ihatemoney/forms.py b/ihatemoney/forms.py index 180619c7..3b4f2773 100644 --- a/ihatemoney/forms.py +++ b/ihatemoney/forms.py @@ -3,12 +3,13 @@ from re import match from types import SimpleNamespace import email_validator -from flask import request +from flask import current_app, request from flask_babel import lazy_gettext as _ from flask_wtf.file import FileAllowed, FileField, FileRequired from flask_wtf.form import FlaskForm from markupsafe import Markup from werkzeug.security import check_password_hash, generate_password_hash +from wtforms.fields import Field from wtforms.fields.core import Label, SelectField, SelectMultipleField from wtforms.fields.html5 import DateField, DecimalField, URLField from wtforms.fields.simple import BooleanField, PasswordField, StringField, SubmitField @@ -221,6 +222,16 @@ class ProjectForm(EditProjectForm): ) raise ValidationError(Markup(message)) + @classmethod + def enable_captcha(cls): + captchaField = StringField(_("Which is a real currency: Euro or Petro dollar?")) + setattr(cls, "captcha", captchaField) + + def validate_captcha(form, field): + if not field.data.lower() == _("euro"): + message = _("Please, validate the captcha to proceed.") + raise ValidationError(Markup(message)) + class DestructiveActionProjectForm(FlaskForm): """Used for any important "delete" action linked to a project: diff --git a/ihatemoney/templates/forms.html b/ihatemoney/templates/forms.html index a9965564..9d8e8c99 100644 --- a/ihatemoney/templates/forms.html +++ b/ihatemoney/templates/forms.html @@ -75,6 +75,9 @@ {{ input(form.name) }} {{ input(form.password) }} {{ input(form.contact_email) }} + {% if config['ENABLE_CAPTCHA'] %} + {{ input(form.captcha) }} + {% endif %} {{ input(form.default_currency) }} {% if not home %} {{ submit(form.submit, home=True) }} @@ -171,7 +174,7 @@ - +
{{ _("More options") }} {% if g.project.default_currency != "XXX" %} diff --git a/ihatemoney/tests/budget_test.py b/ihatemoney/tests/budget_test.py index 1539ece7..1ee3302a 100644 --- a/ihatemoney/tests/budget_test.py +++ b/ihatemoney/tests/budget_test.py @@ -17,6 +17,8 @@ from ihatemoney.tests.common.ihatemoney_testcase import IhatemoneyTestCase from ihatemoney.versioning import LoggingMode + + class BudgetTestCase(IhatemoneyTestCase): def test_notifications(self): """Test that the notifications are sent, and that email addresses @@ -310,6 +312,7 @@ class BudgetTestCase(IhatemoneyTestCase): # project removed self.assertEqual(len(models.Project.query.all()), 0) + def test_bill_placeholder(self): self.post_project("raclette") self.login("raclette") diff --git a/ihatemoney/tests/common/ihatemoney_testcase.py b/ihatemoney/tests/common/ihatemoney_testcase.py index 4a92f616..2c34e1e1 100644 --- a/ihatemoney/tests/common/ihatemoney_testcase.py +++ b/ihatemoney/tests/common/ihatemoney_testcase.py @@ -15,6 +15,7 @@ class BaseTestCase(TestCase): SQLALCHEMY_DATABASE_URI = os.environ.get( "TESTING_SQLALCHEMY_DATABASE_URI", "sqlite://" ) + ENABLE_CAPTCHA = False def create_app(self): # Pass the test object as a configuration. diff --git a/ihatemoney/tests/main_test.py b/ihatemoney/tests/main_test.py index eaf6a01f..418b9928 100644 --- a/ihatemoney/tests/main_test.py +++ b/ihatemoney/tests/main_test.py @@ -31,6 +31,7 @@ class ConfigurationTestCase(BaseTestCase): self.assertTrue(self.app.config["ACTIVATE_DEMO_PROJECT"]) self.assertTrue(self.app.config["ALLOW_PUBLIC_PROJECT_CREATION"]) self.assertFalse(self.app.config["ACTIVATE_ADMIN_DASHBOARD"]) + self.assertFalse(self.app.config["ENABLE_CAPTCHA"]) def test_env_var_configuration_file(self): """Test that settings are loaded from a configuration file specified diff --git a/ihatemoney/web.py b/ihatemoney/web.py index 5af15e08..19f88976 100644 --- a/ihatemoney/web.py +++ b/ihatemoney/web.py @@ -260,9 +260,16 @@ def authenticate(project_id=None): return render_template("authenticate.html", form=form) +def get_project_form(): + if current_app.config.get("ENABLE_CAPTCHA", False): + ProjectForm.enable_captcha() + + return ProjectForm() + + @main.route("/", strict_slashes=False) def home(): - project_form = ProjectForm() + project_form = get_project_form() auth_form = AuthenticationForm() is_demo_project_activated = current_app.config["ACTIVATE_DEMO_PROJECT"] is_public_project_creation_allowed = current_app.config[ @@ -287,7 +294,7 @@ def mobile(): @main.route("/create", methods=["GET", "POST"]) @requires_admin(bypass=("ALLOW_PUBLIC_PROJECT_CREATION", True)) def create_project(): - form = ProjectForm() + form = get_project_form() if request.method == "GET" and "project_id" in request.values: form.name.data = request.values["project_id"]