Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[16.0][ADD] website_recaptcha_v2_login #1075

Closed
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)

Check warning on line 20 in website_recaptcha_v2_form/controllers/main.py

View check run for this annotation

Codecov / codecov/patch

website_recaptcha_v2_form/controllers/main.py#L17-L20

Added lines #L17 - L20 were not covered by tests
if valid:
if template == "web.login":
return super().web_login(values.get("redirect", ""), **kw)

Check warning on line 23 in website_recaptcha_v2_form/controllers/main.py

View check run for this annotation

Codecov / codecov/patch

website_recaptcha_v2_form/controllers/main.py#L23

Added line #L23 was not covered by tests
if template in ("auth_signup.reset_password", "auth_signup.signup"):
return True
except AccessDenied as e:
values.update(

Check warning on line 27 in website_recaptcha_v2_form/controllers/main.py

View check run for this annotation

Codecov / codecov/patch

website_recaptcha_v2_form/controllers/main.py#L25-L27

Added lines #L25 - L27 were not covered by tests
{
"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

Check warning on line 37 in website_recaptcha_v2_form/controllers/main.py

View check run for this annotation

Codecov / codecov/patch

website_recaptcha_v2_form/controllers/main.py#L34-L37

Added lines #L34 - L37 were not covered by tests

@http.route("/web/login", type="http", auth="none")
def web_login(self, redirect=None, **kw):
if request.httprequest.method == "POST":
values = {

Check warning on line 42 in website_recaptcha_v2_form/controllers/main.py

View check run for this annotation

Codecov / codecov/patch

website_recaptcha_v2_form/controllers/main.py#L42

Added line #L42 was not covered by tests
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(

Check warning on line 52 in website_recaptcha_v2_form/controllers/main.py

View check run for this annotation

Codecov / codecov/patch

website_recaptcha_v2_form/controllers/main.py#L52

Added line #L52 was not covered by tests
kw=kw, template="web.login", values=values
)
return super().web_login(redirect, **kw)

Check warning on line 55 in website_recaptcha_v2_form/controllers/main.py

View check run for this annotation

Codecov / codecov/patch

website_recaptcha_v2_form/controllers/main.py#L55

Added line #L55 was not covered by tests


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()

Check warning on line 63 in website_recaptcha_v2_form/controllers/main.py

View check run for this annotation

Codecov / codecov/patch

website_recaptcha_v2_form/controllers/main.py#L63

Added line #L63 was not covered by tests
if request.httprequest.method == "POST":
valid = self.verify_recaptcha_v2(

Check warning on line 65 in website_recaptcha_v2_form/controllers/main.py

View check run for this annotation

Codecov / codecov/patch

website_recaptcha_v2_form/controllers/main.py#L65

Added line #L65 was not covered by tests
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)

Check warning on line 70 in website_recaptcha_v2_form/controllers/main.py

View check run for this annotation

Codecov / codecov/patch

website_recaptcha_v2_form/controllers/main.py#L69-L70

Added lines #L69 - L70 were not covered by tests

@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()

Check warning on line 74 in website_recaptcha_v2_form/controllers/main.py

View check run for this annotation

Codecov / codecov/patch

website_recaptcha_v2_form/controllers/main.py#L74

Added line #L74 was not covered by tests
if request.httprequest.method == "POST":
valid = self.verify_recaptcha_v2(

Check warning on line 76 in website_recaptcha_v2_form/controllers/main.py

View check run for this annotation

Codecov / codecov/patch

website_recaptcha_v2_form/controllers/main.py#L76

Added line #L76 was not covered by tests
kw=kw, template="auth_signup.signup", values=qcontext, args=args
)
if not isinstance(valid, bool):
return valid
return super().web_auth_signup(*args, **kw)

Check warning on line 81 in website_recaptcha_v2_form/controllers/main.py

View check run for this annotation

Codecov / codecov/patch

website_recaptcha_v2_form/controllers/main.py#L80-L81

Added lines #L80 - L81 were not covered by tests
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

Check warning on line 25 in website_recaptcha_v2_form/models/website.py

View check run for this annotation

Codecov / codecov/patch

website_recaptcha_v2_form/models/website.py#L25

Added line #L25 was not covered by tests
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
Loading