Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions setup/website_recaptcha_v2_form/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
1 change: 1 addition & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
odoo-addon-website_recaptcha_v2 @ git+https://github.com/OCA/website.git@refs/pull/1032/head#subdirectory=setup/website_recaptcha_v2
83 changes: 83 additions & 0 deletions website_recaptcha_v2_form/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
====================
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 you to use reCAPTCHA v2 in the login form.

**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 <https://github.com/OCA/website/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 <https://github.com/OCA/website/issues/new?body=module:%20website_recaptcha_v2%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

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

Credits
=======

Authors
~~~~~~~

* Binhex

Contributors
~~~~~~~~~~~~

* `Binhex <https://www.binhex.cloud/>`_:

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 <https://github.com/OCA/website/tree/16.0/website_recaptcha_v2_login>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
2 changes: 2 additions & 0 deletions website_recaptcha_v2_form/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import controllers
28 changes: 28 additions & 0 deletions website_recaptcha_v2_form/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "Website reCAPTCHA v2 form",
"version": "16.0.1.0.0",
"category": "Website",
"depends": ["web", "auth_signup", "website", "website_recaptcha_v2"],
"author": """
Binhex,
Odoo Community Association (OCA)
""",
"license": "AGPL-3",
"website": "https://github.com/OCA/website",
"summary": "Module to add reCAPTCHA v2 to the login form on the website",
"data": [
"views/webclient_templates.xml",
"views/auth_signup_login_templates.xml",
"views/s_website_form.xml",
],
"assets": {
"website.assets_wysiwyg": [
"website_recaptcha_v2_form/static/src/xml/website_form_editor.xml",
"website_recaptcha_v2_form/static/src/snippets/s_website_form/options.js",
],
"web.assets_frontend": [
"website_recaptcha_v2_form/static/src/css/recaptcha.css",
],
},
"installable": True,
}
1 change: 1 addition & 0 deletions website_recaptcha_v2_form/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import main
81 changes: 81 additions & 0 deletions website_recaptcha_v2_form/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import logging

from odoo import _, http
from odoo.exceptions import AccessDenied
from odoo.http import request

from odoo.addons.auth_signup.controllers.main import AuthSignupHome
from odoo.addons.web.controllers.home import SIGN_UP_REQUEST_PARAMS, Home

logger = logging.getLogger(__name__)

SIGN_UP_REQUEST_PARAMS.add("g-recaptcha-response")


class BinhexHome(Home):
def verify_recaptcha_v2(self, args=None, kw=None, template="", values=None):
Website = request.env["website"].sudo()
try:
request.env["ir.http"]._auth_method_public()
valid = Website.get_current_website().valid_recaptcha(values)
if valid:
if template == "web.login":
return super().web_login(values.get("redirect", ""), **kw)
if template in ("auth_signup.reset_password", "auth_signup.signup"):
return True
except AccessDenied as e:
values.update(
{
"error": str(
e.args[0] if len(e.args) > 0 else _("Recaptcha is not valid.")
)
}
)
response = request.render(template, values)
response.headers["X-Frame-Options"] = "SAMEORIGIN"
response.headers["Content-Security-Policy"] = "frame-ancestors 'self'"
return response

@http.route("/web/login", type="http", auth="none")
def web_login(self, redirect=None, **kw):
if request.httprequest.method == "POST":
values = {
k: v for k, v in request.params.items() if k in SIGN_UP_REQUEST_PARAMS
}
# Checking that if the request comes from the creation of the account,
# that the recaptcha is not checked again to avoid errors.

if (
values.get("confirm_password", "") == ""
and request.httprequest.url.find("web/signup") == -1
):
return self.verify_recaptcha_v2(
kw=kw, template="web.login", values=values
)
return super().web_login(redirect, **kw)


class BinhexAuthSignupHome(AuthSignupHome):
@http.route(
"/web/reset_password", type="http", auth="public", website=True, sitemap=False
)
def web_auth_reset_password(self, *args, **kw):
qcontext = self.get_auth_signup_qcontext()
if request.httprequest.method == "POST":
valid = self.verify_recaptcha_v2(
kw=kw, template="auth_signup.reset_password", values=qcontext, args=args
)
if not isinstance(valid, bool):
return valid
return super().web_auth_reset_password(*args, **kw)

@http.route("/web/signup", type="http", auth="public", website=True, sitemap=False)
def web_auth_signup(self, *args, **kw):
qcontext = self.get_auth_signup_qcontext()
if request.httprequest.method == "POST":
valid = self.verify_recaptcha_v2(
kw=kw, template="auth_signup.signup", values=qcontext, args=args
)
if not isinstance(valid, bool):
return valid
return super().web_auth_signup(*args, **kw)
28 changes: 28 additions & 0 deletions website_recaptcha_v2_form/i18n/es.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * binhex_website_recaptcha_v2
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-12-02 16:23+0000\n"
"PO-Revision-Date: 2024-12-02 16:23+0000\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: binhex_website_recaptcha_v2
#. odoo-python
#: code:addons/binhex_website_recaptcha_v2/controllers/main.py:0
#, python-format
msgid "Recaptcha is not valid."
msgstr "Recaptcha no es válido."

#. module: binhex_website_recaptcha_v2
#: model:ir.model,name:binhex_website_recaptcha_v2.model_website
msgid "Website"
msgstr "Sitio web"
1 change: 1 addition & 0 deletions website_recaptcha_v2_form/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import website
25 changes: 25 additions & 0 deletions website_recaptcha_v2_form/models/website.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from odoo import api, models
from odoo.exceptions import AccessDenied


class Website(models.Model):
_inherit = "website"

# --------------------------------------------------
# METHODS
# --------------------------------------------------
"""
Validating that the recaptcha sent is correct
@params:
kw: Data sent from the form
"""

def valid_recaptcha(self, values):
valid, message = self.is_recaptcha_v2_valid(values)
if not valid:
raise AccessDenied(message)
return True

@api.model
def get_recaptcha_v2_site_key(self):
return self.sudo().get_current_website().recaptcha_v2_site_key
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions website_recaptcha_v2_form/static/src/css/recaptcha.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions website_recaptcha_v2_form/static/src/css/recaptcha.css.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions website_recaptcha_v2_form/static/src/scss/recaptcha.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
div.s_website_form_recaptcha_v2 {
> div.g-recaptcha {
margin-left: 18% !important;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
odoo.define("website_recaptcha_v2_form.form_editor", function (require) {
"use strict";

var options = require("web_editor.snippets.options");
const core = require("web.core");
const rpc = require("web.rpc");
const qweb = core.qweb;
require("website.form_editor");

options.registry.WebsiteFormEditor.include({
willStart: async function () {
var res = this._super(...arguments);
this.recaptcha_site_key = await rpc.query({
model: "website",
method: "get_recaptcha_v2_site_key",
});
return res;
},
toggleRecaptchaV2: async function () {
const recaptchaV2 = this.$target[0].querySelector(
".s_website_form_recaptcha_v2"
);
if (recaptchaV2) {
recaptchaV2.remove();
} else {
const legal = qweb.render("website_recaptcha_v2_form.recaptcha_v2", {
recaptcha_site_key: this.recaptcha_site_key,
});
this.$target.find(".s_website_form_submit").before(legal);
}
},
_computeWidgetState: function (methodName, params) {
switch (methodName) {
case "toggleRecaptchaV2":
return (
!this.$target[0].querySelector(
".s_website_form_recaptcha_v2"
) || ""
);
}
return this._super(...arguments);
},
});
});
11 changes: 11 additions & 0 deletions website_recaptcha_v2_form/static/src/xml/website_form_editor.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">

<t t-name="website_recaptcha_v2_form.recaptcha_v2">
<div class="col-12 s_website_form_recaptcha_v2" data-name="Recaptcha v2 Legal">
<div class="g-recaptcha" t-att-data-sitekey="recaptcha_site_key" />
<script src="https://www.recaptcha.net/recaptcha/api.js" async="1" />
</div>
</t>

</templates>
1 change: 1 addition & 0 deletions website_recaptcha_v2_form/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_recaptcha
34 changes: 34 additions & 0 deletions website_recaptcha_v2_form/tests/test_recaptcha.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from unittest import mock

from odoo.exceptions import AccessDenied
from odoo.tests import common

imp_requests = "odoo.addons.website_recaptcha_v2.models.website.requests"


class TestModule(common.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_valid(self, requests_mock):
requests_mock.post().json.return_value = {"success": True}
result = self.website.valid_recaptcha(
{"g-recaptcha-response": "dummy_response"}
)
self.assertTrue(result)

@mock.patch(imp_requests)
def test_captcha_not_valid(self, requests_mock):
requests_mock.post().json.return_value = {"success": False}
with self.assertRaises(AccessDenied):
self.website.valid_recaptcha({"g-recaptcha-response": ""})
14 changes: 14 additions & 0 deletions website_recaptcha_v2_form/views/auth_signup_login_templates.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<template id="binhex_signup" inherit_id="auth_signup.signup">
<xpath expr="//p[hasclass('alert-danger')]" position="before">
<t t-call="website_recaptcha_v2.recaptcha_widget" />
</xpath>
</template>

<template id="binhex_reset_password" inherit_id="auth_signup.reset_password">
<xpath expr="//p[hasclass('alert-danger')]" position="before">
<t t-call="website_recaptcha_v2.recaptcha_widget" />
</xpath>
</template>
</odoo>
Loading