diff --git a/setup/website_recaptcha_v2/odoo/addons/website_recaptcha_v2 b/setup/website_recaptcha_v2/odoo/addons/website_recaptcha_v2 new file mode 120000 index 0000000..8ea48c4 --- /dev/null +++ b/setup/website_recaptcha_v2/odoo/addons/website_recaptcha_v2 @@ -0,0 +1 @@ +../../../../website_recaptcha_v2 \ No newline at end of file diff --git a/setup/website_recaptcha_v2/setup.py b/setup/website_recaptcha_v2/setup.py new file mode 100644 index 0000000..28c57bb --- /dev/null +++ b/setup/website_recaptcha_v2/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/website_recaptcha_v2_form/odoo/addons/website_recaptcha_v2_form b/setup/website_recaptcha_v2_form/odoo/addons/website_recaptcha_v2_form new file mode 120000 index 0000000..c5585f7 --- /dev/null +++ b/setup/website_recaptcha_v2_form/odoo/addons/website_recaptcha_v2_form @@ -0,0 +1 @@ +../../../../website_recaptcha_v2_form \ No newline at end of file diff --git a/setup/website_recaptcha_v2_form/setup.py b/setup/website_recaptcha_v2_form/setup.py new file mode 100644 index 0000000..28c57bb --- /dev/null +++ b/setup/website_recaptcha_v2_form/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/website_recaptcha_v2/README.rst b/website_recaptcha_v2/README.rst new file mode 100644 index 0000000..bb3b7e2 --- /dev/null +++ b/website_recaptcha_v2/README.rst @@ -0,0 +1,106 @@ +==================== +Website reCAPTCHA v2 +==================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:55856dbbdf9c9efc1b9b1ebbb33638a0018eda0d91bd6c8c9e30805aa8f2e5b0 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fwebsite-lightgray.png?logo=github + :target: https://github.com/OCA/website/tree/16.0/website_recaptcha_v2 + :alt: OCA/website +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/website-16-0/website-16-0-website_recaptcha_v2 + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/website&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows to use reCAPTCHA v2 on website forms. + +It is a helper module that only provides the widget and the validation logic +which can be used by other modules to actually display it on website forms and +check whether the user entry is valid. + +This module originally comes from ``website_recaptcha_reloaded`` from Tech +Receptives, which itself comes from ``website_recaptcha`` from Elico Corp. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +reCAPTCHA is configured in Settings > Website. It can be enabled or disabled +using the checkbox, and the site and secret keys can be defined there when it +is enabled. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Elico Corp +* Tech Receptives +* Coop IT Easy SC + +Contributors +~~~~~~~~~~~~ + +* `Elico Corp `_: + + * Augustin Cisterne-Kaas +* `Tech Receptives `_: + + * Parthiv Patel + * Hardik Kalariya + +* `Coop IT Easy SC `_: + + * Houssine Bakkali + * Robin Keunen + * Carmen Bianca Bakker + * Victor Champonnois + * hugues de keyzer + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/website `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/website_recaptcha_v2/__init__.py b/website_recaptcha_v2/__init__.py new file mode 100644 index 0000000..d4f3369 --- /dev/null +++ b/website_recaptcha_v2/__init__.py @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2010-2014 Elico Corp +# SPDX-FileContributor: Augustin Cisterne-Kaas +# SPDX-FileCopyrightText: 2015 Tech-Receptives Solutions Pvt. Ltd. +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from . import models diff --git a/website_recaptcha_v2/__manifest__.py b/website_recaptcha_v2/__manifest__.py new file mode 100644 index 0000000..9aba64b --- /dev/null +++ b/website_recaptcha_v2/__manifest__.py @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: 2010-2014 Elico Corp +# SPDX-FileContributor: Augustin Cisterne-Kaas +# SPDX-FileCopyrightText: 2015 Tech-Receptives Solutions Pvt. Ltd. +# SPDX-FileCopyrightText: 2019 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +{ + "name": "Website reCAPTCHA v2", + "version": "15.0.1.0.0", + "category": "Website", + "depends": ["website"], + "author": ( + "Elico Corp, Tech Receptives, Coop IT Easy SC, " + "Odoo Community Association (OCA)" + ), + "license": "AGPL-3", + "website": "https://github.com/OCA/website", + "summary": "Helper module to add reCAPTCHA v2 to website forms", + "data": [ + "views/res_config_settings_view.xml", + "views/website_templates.xml", + ], + "demo": [ + "demo/demo.xml", + ], +} diff --git a/website_recaptcha_v2/demo/demo.xml b/website_recaptcha_v2/demo/demo.xml new file mode 100644 index 0000000..8630a6f --- /dev/null +++ b/website_recaptcha_v2/demo/demo.xml @@ -0,0 +1,22 @@ + + + + + + True + + 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI + 6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe + + + diff --git a/website_recaptcha_v2/i18n/it.po b/website_recaptcha_v2/i18n/it.po new file mode 100644 index 0000000..3edd13a --- /dev/null +++ b/website_recaptcha_v2/i18n/it.po @@ -0,0 +1,144 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * website_recaptcha_v2 +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-12-04 12:06+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.6.2\n" + +#. module: website_recaptcha_v2 +#: model_terms:ir.ui.view,arch_db:website_recaptcha_v2.res_config_settings_view_form +msgid "" +"\n" +" Get reCAPTCHA v2 keys" +msgstr "" +"\n" +" Ottieni chiavi reCAPTCHA v2" + +#. module: website_recaptcha_v2 +#: model_terms:ir.ui.view,arch_db:website_recaptcha_v2.res_config_settings_view_form +msgid "" +"" +msgstr "" +"" + +#. module: website_recaptcha_v2 +#: model:ir.model,name:website_recaptcha_v2.model_res_config_settings +msgid "Config Settings" +msgstr "Impostazioni configurazione" + +#. module: website_recaptcha_v2 +#: model:ir.model.fields,field_description:website_recaptcha_v2.field_res_config_settings__recaptcha_v2_enabled +#: model:ir.model.fields,field_description:website_recaptcha_v2.field_website__recaptcha_v2_enabled +msgid "Enable reCAPTCHA v2" +msgstr "Abilita reCAPTCHA v2" + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "No response given." +msgstr "Nessuna risposta data." + +#. module: website_recaptcha_v2 +#: model_terms:ir.ui.view,arch_db:website_recaptcha_v2.res_config_settings_view_form +msgid "Protect your forms using reCAPTCHA v2" +msgstr "Proteggere i propri moduli usando reCAPTCHA v2" + +#. module: website_recaptcha_v2 +#: model:ir.model.fields,field_description:website_recaptcha_v2.field_res_config_settings__recaptcha_v2_secret_key +#: model:ir.model.fields,field_description:website_recaptcha_v2.field_website__recaptcha_v2_secret_key +msgid "Secret Key (v2)" +msgstr "Chiave segreta (v2)" + +#. module: website_recaptcha_v2 +#: model:ir.model.fields,field_description:website_recaptcha_v2.field_res_config_settings__recaptcha_v2_site_key +#: model:ir.model.fields,field_description:website_recaptcha_v2.field_website__recaptcha_v2_site_key +msgid "Site Key (v2)" +msgstr "Chiave sito (v2)" + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "The challenge was not successfully completed." +msgstr "La verifica non è stata completata con successo." + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "The request is invalid or malformed." +msgstr "La richiesta non è valida o malformato." + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "" +"The response is no longer valid: either is too old or has been used " +"previously." +msgstr "" +"La risposta non è più valida: è troppo vecchia o è già stata utilizzata." + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "The response parameter is invalid or malformed." +msgstr "Il parametro della risposta non è valido o malformato." + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "The response parameter is missing." +msgstr "Manca il parametro della risposta." + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "The secret parameter is invalid or malformed." +msgstr "Il parametro segreto non è valido o malformato." + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "The secret parameter is missing." +msgstr "Manca il parametro segreto." + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "Unknown reCAPTCHA error (error code: {errorcode})." +msgstr "Errore reCAPTCHA sconosciuto (codice errore: {errorcode})." + +#. module: website_recaptcha_v2 +#: model:ir.model,name:website_recaptcha_v2.model_website +msgid "Website" +msgstr "Sito web" + +#. module: website_recaptcha_v2 +#: model_terms:ir.ui.view,arch_db:website_recaptcha_v2.res_config_settings_view_form +msgid "reCAPTCHA icon" +msgstr "Icona reCAPTCHA" + +#. module: website_recaptcha_v2 +#: model_terms:ir.ui.view,arch_db:website_recaptcha_v2.res_config_settings_view_form +msgid "reCAPTCHA v2" +msgstr "reCAPTCHA v2" diff --git a/website_recaptcha_v2/i18n/website_recaptcha_v2.pot b/website_recaptcha_v2/i18n/website_recaptcha_v2.pot new file mode 100644 index 0000000..cac362e --- /dev/null +++ b/website_recaptcha_v2/i18n/website_recaptcha_v2.pot @@ -0,0 +1,136 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * website_recaptcha_v2 +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: website_recaptcha_v2 +#: model_terms:ir.ui.view,arch_db:website_recaptcha_v2.res_config_settings_view_form +msgid "" +"\n" +" Get reCAPTCHA v2 keys" +msgstr "" + +#. module: website_recaptcha_v2 +#: model_terms:ir.ui.view,arch_db:website_recaptcha_v2.res_config_settings_view_form +msgid "" +"" +msgstr "" + +#. module: website_recaptcha_v2 +#: model:ir.model,name:website_recaptcha_v2.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: website_recaptcha_v2 +#: model:ir.model.fields,field_description:website_recaptcha_v2.field_res_config_settings__recaptcha_v2_enabled +#: model:ir.model.fields,field_description:website_recaptcha_v2.field_website__recaptcha_v2_enabled +msgid "Enable reCAPTCHA v2" +msgstr "" + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "No response given." +msgstr "" + +#. module: website_recaptcha_v2 +#: model_terms:ir.ui.view,arch_db:website_recaptcha_v2.res_config_settings_view_form +msgid "Protect your forms using reCAPTCHA v2" +msgstr "" + +#. module: website_recaptcha_v2 +#: model:ir.model.fields,field_description:website_recaptcha_v2.field_res_config_settings__recaptcha_v2_secret_key +#: model:ir.model.fields,field_description:website_recaptcha_v2.field_website__recaptcha_v2_secret_key +msgid "Secret Key (v2)" +msgstr "" + +#. module: website_recaptcha_v2 +#: model:ir.model.fields,field_description:website_recaptcha_v2.field_res_config_settings__recaptcha_v2_site_key +#: model:ir.model.fields,field_description:website_recaptcha_v2.field_website__recaptcha_v2_site_key +msgid "Site Key (v2)" +msgstr "" + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "The challenge was not successfully completed." +msgstr "" + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "The request is invalid or malformed." +msgstr "" + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "" +"The response is no longer valid: either is too old or has been used " +"previously." +msgstr "" + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "The response parameter is invalid or malformed." +msgstr "" + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "The response parameter is missing." +msgstr "" + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "The secret parameter is invalid or malformed." +msgstr "" + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "The secret parameter is missing." +msgstr "" + +#. module: website_recaptcha_v2 +#. odoo-python +#: code:addons/website_recaptcha_v2/models/website.py:0 +#, python-format +msgid "Unknown reCAPTCHA error (error code: {errorcode})." +msgstr "" + +#. module: website_recaptcha_v2 +#: model:ir.model,name:website_recaptcha_v2.model_website +msgid "Website" +msgstr "" + +#. module: website_recaptcha_v2 +#: model_terms:ir.ui.view,arch_db:website_recaptcha_v2.res_config_settings_view_form +msgid "reCAPTCHA icon" +msgstr "" + +#. module: website_recaptcha_v2 +#: model_terms:ir.ui.view,arch_db:website_recaptcha_v2.res_config_settings_view_form +msgid "reCAPTCHA v2" +msgstr "" diff --git a/website_recaptcha_v2/models/__init__.py b/website_recaptcha_v2/models/__init__.py new file mode 100644 index 0000000..2224c42 --- /dev/null +++ b/website_recaptcha_v2/models/__init__.py @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: 2010-2014 Elico Corp +# SPDX-FileContributor: Augustin Cisterne-Kaas +# SPDX-FileCopyrightText: 2015 Tech-Receptives Solutions Pvt. Ltd. +# SPDX-FileCopyrightText: 2022 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from . import res_config_settings +from . import website diff --git a/website_recaptcha_v2/models/res_config_settings.py b/website_recaptcha_v2/models/res_config_settings.py new file mode 100644 index 0000000..fa2e0cf --- /dev/null +++ b/website_recaptcha_v2/models/res_config_settings.py @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: 2010-2014 Elico Corp +# SPDX-FileContributor: Augustin Cisterne-Kaas +# SPDX-FileCopyrightText: 2015 Tech-Receptives Solutions Pvt. Ltd. +# SPDX-FileCopyrightText: 2019 Simone Orsi - Camptocamp SA +# SPDX-FileCopyrightText: 2019 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + recaptcha_v2_enabled = fields.Boolean( + related="website_id.recaptcha_v2_enabled", readonly=False + ) + recaptcha_v2_site_key = fields.Char( + related="website_id.recaptcha_v2_site_key", readonly=False + ) + recaptcha_v2_secret_key = fields.Char( + related="website_id.recaptcha_v2_secret_key", readonly=False + ) diff --git a/website_recaptcha_v2/models/website.py b/website_recaptcha_v2/models/website.py new file mode 100644 index 0000000..01ca210 --- /dev/null +++ b/website_recaptcha_v2/models/website.py @@ -0,0 +1,74 @@ +# SPDX-FileCopyrightText: 2010-2014 Elico Corp +# SPDX-FileContributor: Augustin Cisterne-Kaas +# SPDX-FileCopyrightText: 2015 Tech-Receptives Solutions Pvt. Ltd. +# SPDX-FileCopyrightText: 2019 Simone Orsi - Camptocamp SA +# SPDX-FileCopyrightText: 2019 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +import requests + +from odoo import _, api, fields, models + +RECAPTCHA_API_URL = "https://www.recaptcha.net/recaptcha/api/siteverify" +RECAPTCHA_API_TIMEOUT = 30 + + +class Website(models.Model): + _inherit = "website" + + recaptcha_v2_enabled = fields.Boolean("Enable reCAPTCHA v2") + recaptcha_v2_site_key = fields.Char("Site Key (v2)") + recaptcha_v2_secret_key = fields.Char("Secret Key (v2)") + + @api.model + def _get_error_message(self, errorcode=None): + mapping = { + "missing-input-secret": _("The secret parameter is missing."), + "invalid-input-secret": _("The secret parameter is invalid or malformed."), + "missing-input-response": _("The response parameter is missing."), + "invalid-input-response": _( + "The response parameter is invalid or malformed." + ), + "bad-request": _("The request is invalid or malformed."), + "timeout-or-duplicate": _( + "The response is no longer valid: either is too old or has " + "been used previously." + ), + } + return mapping.get( + errorcode, + _("Unknown reCAPTCHA error (error code: %(errorcode)s).") + % {"errorcode": errorcode}, + ) + + def is_recaptcha_v2_valid(self, form_values): + """ + Checks whether the reCAPTCHA v2 challenge has been correctly solved. + + form_values must be a dictionary containing the form values. + + Returns a (bool, str) tuple. The first element tells whether the + CAPTCHA is valid or not. The second is the error message when + applicable (or an empty string). + + If reCAPTCHA is disabled in the settings, this method behaves as if + the CAPTCHA was correctly solved, but without doing any check. + """ + if not self.recaptcha_v2_enabled: + return (True, "") + response = form_values.get("g-recaptcha-response") + if not response: + return (False, _("No response given.")) + get_res = {"secret": self.recaptcha_v2_secret_key, "response": response} + res = requests.post( + RECAPTCHA_API_URL, data=get_res, timeout=RECAPTCHA_API_TIMEOUT + ).json() + error_msg = "\n".join( + self._get_error_message(error) for error in res.get("error-codes", []) + ) + if error_msg: + return (False, error_msg) + if not res.get("success"): + return (False, _("The challenge was not successfully completed.")) + return (True, "") diff --git a/website_recaptcha_v2/readme/CONFIGURE.rst b/website_recaptcha_v2/readme/CONFIGURE.rst new file mode 100644 index 0000000..14e14fe --- /dev/null +++ b/website_recaptcha_v2/readme/CONFIGURE.rst @@ -0,0 +1,3 @@ +reCAPTCHA is configured in Settings > Website. It can be enabled or disabled +using the checkbox, and the site and secret keys can be defined there when it +is enabled. diff --git a/website_recaptcha_v2/readme/CONTRIBUTORS.rst b/website_recaptcha_v2/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..caade69 --- /dev/null +++ b/website_recaptcha_v2/readme/CONTRIBUTORS.rst @@ -0,0 +1,15 @@ +* `Elico Corp `_: + + * Augustin Cisterne-Kaas +* `Tech Receptives `_: + + * Parthiv Patel + * Hardik Kalariya + +* `Coop IT Easy SC `_: + + * Houssine Bakkali + * Robin Keunen + * Carmen Bianca Bakker + * Victor Champonnois + * hugues de keyzer diff --git a/website_recaptcha_v2/readme/DESCRIPTION.rst b/website_recaptcha_v2/readme/DESCRIPTION.rst new file mode 100644 index 0000000..3184f04 --- /dev/null +++ b/website_recaptcha_v2/readme/DESCRIPTION.rst @@ -0,0 +1,8 @@ +This module allows to use reCAPTCHA v2 on website forms. + +It is a helper module that only provides the widget and the validation logic +which can be used by other modules to actually display it on website forms and +check whether the user entry is valid. + +This module originally comes from ``website_recaptcha_reloaded`` from Tech +Receptives, which itself comes from ``website_recaptcha`` from Elico Corp. diff --git a/website_recaptcha_v2/static/description/icon.png b/website_recaptcha_v2/static/description/icon.png new file mode 100644 index 0000000..3a0328b Binary files /dev/null and b/website_recaptcha_v2/static/description/icon.png differ diff --git a/website_recaptcha_v2/static/description/index.html b/website_recaptcha_v2/static/description/index.html new file mode 100644 index 0000000..7462acf --- /dev/null +++ b/website_recaptcha_v2/static/description/index.html @@ -0,0 +1,451 @@ + + + + + + +Website reCAPTCHA v2 + + + +
+

Website reCAPTCHA v2

+ + +

Beta License: AGPL-3 OCA/website Translate me on Weblate Try me on Runboat

+

This module allows to use reCAPTCHA v2 on website forms.

+

It is a helper module that only provides the widget and the validation logic +which can be used by other modules to actually display it on website forms and +check whether the user entry is valid.

+

This module originally comes from website_recaptcha_reloaded from Tech +Receptives, which itself comes from website_recaptcha from Elico Corp.

+

Table of contents

+ +
+

Configuration

+

reCAPTCHA is configured in Settings > Website. It can be enabled or disabled +using the checkbox, and the site and secret keys can be defined there when it +is enabled.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Elico Corp
  • +
  • Tech Receptives
  • +
  • Coop IT Easy SC
  • +
+
+
+

Contributors

+
    +
  • Elico Corp:
      +
    • Augustin Cisterne-Kaas
    • +
    +
  • +
  • Tech Receptives:
      +
    • Parthiv Patel
    • +
    • Hardik Kalariya
    • +
    +
  • +
  • Coop IT Easy SC:
      +
    • Houssine Bakkali
    • +
    • Robin Keunen
    • +
    • Carmen Bianca Bakker
    • +
    • Victor Champonnois
    • +
    • hugues de keyzer
    • +
    +
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/website project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/website_recaptcha_v2/static/description/recaptcha_ico.png b/website_recaptcha_v2/static/description/recaptcha_ico.png new file mode 100644 index 0000000..65f4e01 Binary files /dev/null and b/website_recaptcha_v2/static/description/recaptcha_ico.png differ diff --git a/website_recaptcha_v2/tests/__init__.py b/website_recaptcha_v2/tests/__init__.py new file mode 100644 index 0000000..85b4755 --- /dev/null +++ b/website_recaptcha_v2/tests/__init__.py @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2022 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from . import test_recaptcha diff --git a/website_recaptcha_v2/tests/test_recaptcha.py b/website_recaptcha_v2/tests/test_recaptcha.py new file mode 100644 index 0000000..4af83ac --- /dev/null +++ b/website_recaptcha_v2/tests/test_recaptcha.py @@ -0,0 +1,128 @@ +# SPDX-FileCopyrightText: 2022 Coop IT Easy SC +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +from unittest import mock + +from odoo.tests.common import TransactionCase + +imp_requests = "odoo.addons.website_recaptcha_v2.models.website.requests" + + +class TestRecaptcha(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.website = cls.env.ref("website.default_website") + cls.website.write( + { + "recaptcha_v2_enabled": True, + "recaptcha_v2_site_key": "test-site", + "recaptcha_v2_secret_key": "test-secret", + } + ) + + @mock.patch(imp_requests) + def test_captcha_http_request(self, requests_mock): + self.website.is_recaptcha_v2_valid({"g-recaptcha-response": "dummy_response"}) + requests_mock.post.assert_called_once_with( + "https://www.recaptcha.net/recaptcha/api/siteverify", + data={ + "secret": "test-secret", + "response": "dummy_response", + }, + timeout=30, + ) + + @mock.patch(imp_requests) + def test_captcha_valid(self, requests_mock): + requests_mock.post().json.return_value = {"success": True} + result, error_msg = self.website.is_recaptcha_v2_valid( + {"g-recaptcha-response": "dummy_response"} + ) + self.assertTrue(result) + self.assertEqual(error_msg, "") + + @mock.patch(imp_requests) + def test_captcha_single_error(self, requests_mock): + requests_mock.post().json.return_value = { + "error-codes": ["missing-input-secret"] + } + result, error_msg = self.website.is_recaptcha_v2_valid( + {"g-recaptcha-response": "dummy_response"} + ) + self.assertFalse(result) + self.assertEqual(error_msg, "The secret parameter is missing.") + + @mock.patch(imp_requests) + def test_captcha_multiple_errors(self, requests_mock): + requests_mock.post().json.return_value = { + "error-codes": ["invalid-input-secret", "missing-input-response"] + } + result, error_msg = self.website.is_recaptcha_v2_valid( + {"g-recaptcha-response": "dummy_response"} + ) + self.assertFalse(result) + self.assertEqual( + error_msg, + "The secret parameter is invalid or malformed.\n" + "The response parameter is missing.", + ) + + @mock.patch(imp_requests) + def test_captcha_false_success(self, requests_mock): + requests_mock.post().json.return_value = {"success": False} + result, error_msg = self.website.is_recaptcha_v2_valid( + {"g-recaptcha-response": "dummy_response"} + ) + self.assertFalse(result) + self.assertEqual(error_msg, "The challenge was not successfully completed.") + + @mock.patch(imp_requests) + def test_captcha_empty_response(self, requests_mock): + requests_mock.post().json.return_value = {} + result, error_msg = self.website.is_recaptcha_v2_valid( + {"g-recaptcha-response": "dummy_response"} + ) + self.assertFalse(result) + self.assertEqual(error_msg, "The challenge was not successfully completed.") + + @mock.patch(imp_requests) + def test_captcha_unknown_error(self, requests_mock): + requests_mock.post().json.return_value = {"error-codes": ["unknown-error"]} + result, error_msg = self.website.is_recaptcha_v2_valid( + {"g-recaptcha-response": "dummy_response"} + ) + self.assertFalse(result) + self.assertEqual( + error_msg, "Unknown reCAPTCHA error (error code: unknown-error)." + ) + + @mock.patch(imp_requests) + def test_captcha_no_errors_and_success(self, requests_mock): + requests_mock.post().json.return_value = { + "error-codes": [], + "success": True, + } + result, error_msg = self.website.is_recaptcha_v2_valid( + {"g-recaptcha-response": "dummy_response"} + ) + self.assertTrue(result) + self.assertEqual(error_msg, "") + + def test_captcha_no_response(self): + result, error_msg = self.website.is_recaptcha_v2_valid({}) + self.assertFalse(result) + self.assertEqual(error_msg, "No response given.") + + @mock.patch(imp_requests) + def test_captcha_disabled(self, requests_mock): + self.env["ir.config_parameter"].sudo().set_param( + "portal_recaptcha.recaptcha_v2_enabled", False + ) + result, error_msg = self.website.is_recaptcha_v2_valid( + {"g-recaptcha-response": "dummy_response"} + ) + self.assertTrue(result) + self.assertEqual(error_msg, "") + requests_mock.assert_not_called() diff --git a/website_recaptcha_v2/views/res_config_settings_view.xml b/website_recaptcha_v2/views/res_config_settings_view.xml new file mode 100644 index 0000000..cdfa539 --- /dev/null +++ b/website_recaptcha_v2/views/res_config_settings_view.xml @@ -0,0 +1,81 @@ + + + + + + Website settings + res.config.settings + + + +

reCAPTCHA v2

+
+
+
+ +
+
+ reCAPTCHA icon +
+
+
+
+
+
+ +
diff --git a/website_recaptcha_v2/views/website_templates.xml b/website_recaptcha_v2/views/website_templates.xml new file mode 100644 index 0000000..f4b2781 --- /dev/null +++ b/website_recaptcha_v2/views/website_templates.xml @@ -0,0 +1,22 @@ + + + + +