Skip to content

Commit 33c2c59

Browse files
authored
[REF] base_libre_captcha: fetch values from settings (#79)
* [REF] fetch values from settings * [ADD] website_librecaptcha * [REF] feedback and check server validity
1 parent 2d603e7 commit 33c2c59

20 files changed

+501
-54
lines changed

base_librecaptcha/__manifest__.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@
44
"author": "Onestein",
55
"website": "https://www.onestein.nl",
66
"category": "Tools",
7-
"version": "16.0.1.0.0",
7+
"version": "16.0.1.1.0",
88
"license": "AGPL-3",
99
"depends": ["base", "portal"],
1010
"external_dependencies": {"python": ["requests"]},
11-
"data": ["data/ir_config_parameters_data.xml", "templates/captcha.xml"],
11+
"data": [
12+
"data/ir_cron_data.xml",
13+
"templates/captcha.xml",
14+
"templates/auth_signup_login_templates.xml",
15+
"views/res_config_settings_views.xml",
16+
],
1217
"assets": {"web.assets_frontend": ["base_librecaptcha/static/src/js/captcha.js"]},
1318
}
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
from . import base
12
from . import main

base_librecaptcha/controllers/base.py

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from odoo import http
2+
from odoo.exceptions import UserError
3+
from odoo.http import request
4+
5+
from odoo.addons.auth_signup.controllers.main import AuthSignupHome
6+
from odoo.addons.web.controllers.home import SIGN_UP_REQUEST_PARAMS
7+
from odoo.addons.web.controllers.utils import ensure_db
8+
9+
10+
class CaptchaAuthSignupHome(AuthSignupHome):
11+
def get_auth_signup_config(self):
12+
values = super().get_auth_signup_config()
13+
values["captcha_enabled"] = request.env["librecaptcha"].is_enabled()
14+
return values
15+
16+
def _prepare_signup_values(self, qcontext):
17+
if not qcontext.get("captcha_enabled"):
18+
return super()._prepare_signup_values(qcontext)
19+
20+
request.env["librecaptcha"].answer(
21+
request.params.get("captcha_id"),
22+
request.params.get("captcha_answer"),
23+
raise_exception=True,
24+
)
25+
26+
return super()._prepare_signup_values(qcontext)
27+
28+
@http.route()
29+
def web_login(self, *args, **kw):
30+
ensure_db()
31+
if request.env.uid is None:
32+
if request.session.uid is None:
33+
# no user -> auth=public with specific website public user
34+
request.env["ir.http"]._auth_method_public()
35+
else:
36+
# auth=user
37+
request.update_env(user=request.session.uid)
38+
39+
if not request.env["librecaptcha"].is_enabled():
40+
return super().web_login(*args, **kw)
41+
42+
if request.httprequest.method == "POST":
43+
if request.session.uid:
44+
return super().web_login(*args, **kw)
45+
try:
46+
request.env["librecaptcha"].answer(
47+
request.params.get("captcha_id"),
48+
request.params.get("captcha_answer"),
49+
raise_exception=True,
50+
)
51+
except UserError as e:
52+
values = {
53+
k: v
54+
for k, v in request.params.items()
55+
if k in SIGN_UP_REQUEST_PARAMS
56+
}
57+
values["error"] = str(e)
58+
values["captcha_enabled"] = True
59+
60+
response = request.render("web.login", values)
61+
response.headers["Cache-Control"] = "no-cache"
62+
response.headers["X-Frame-Options"] = "SAMEORIGIN"
63+
response.headers["Content-Security-Policy"] = "frame-ancestors 'self'"
64+
return response
65+
66+
return super().web_login(*args, **kw)

base_librecaptcha/controllers/main.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,14 @@ def media(self, **kw):
1414
"""Obscure that we're using librecaptcha and what server 🤫
1515
This we also can keep the lc server unexposed 🤩
1616
"""
17-
mimetype = (
18-
request.env["ir.config_parameter"]
19-
.sudo()
20-
.get_param("base_librecaptcha.media", "image/gif")
21-
)
2217
media = request.env["librecaptcha"].media(kw.get("id"))
2318
if not media:
2419
return request.not_found()
20+
mimetype = request.env["librecaptcha"]._get_config().get("media")
2521
return _send_file(
26-
BytesIO(media), mimetype=mimetype, environ=request.httprequest.environ
22+
BytesIO(media),
23+
mimetype=mimetype,
24+
environ=request.httprequest.environ,
2725
)
2826

2927
@route("/captcha", type="json", auth="public", methods=["POST"])

base_librecaptcha/data/ir_config_parameters_data.xml

-17
This file was deleted.
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<odoo noupdate="1">
3+
4+
<record id="ir_cron_libre_captcha_check_server" model="ir.cron">
5+
<field name="name">LibreCaptcha: Check valid server</field>
6+
<field name="model_id" ref="model_res_company"/>
7+
<field name="state">code</field>
8+
<field name="code">model._cron_librecaptcha_check_server()</field>
9+
<field name="user_id" ref="base.user_root"/>
10+
<field name="interval_number">1</field>
11+
<field name="interval_type">hours</field>
12+
<field name="numbercall">-1</field>
13+
<field name="doall" eval="False"/>
14+
</record>
15+
16+
</odoo>

base_librecaptcha/models/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
from . import librecaptcha
2+
from . import res_company
3+
from . import res_config_settings
+42-26
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import json
12
import logging
23

34
import requests
45

5-
from odoo import _, models
6-
from odoo.exceptions import ValidationError
6+
from odoo import _, http, models
7+
from odoo.exceptions import UserError
78

89
_logger = logging.getLogger(__name__)
910

@@ -13,41 +14,36 @@ class LibreCaptcha(models.AbstractModel):
1314
_description = "LibreCaptcha"
1415

1516
def captcha(self):
16-
url = self.env["ir.config_parameter"].sudo().get_param("base_librecaptcha.url")
17-
level = (
18-
self.env["ir.config_parameter"]
19-
.sudo()
20-
.get_param("base_librecaptcha.level", "hard")
21-
)
22-
media = (
23-
self.env["ir.config_parameter"]
24-
.sudo()
25-
.get_param("base_librecaptcha.media", "image/gif")
26-
)
17+
if not self.is_enabled():
18+
return http.request.make_response(
19+
data=json.dumps({"error": "Captcha is not enabled"}),
20+
status=400,
21+
)
22+
23+
url, level, media, input_type = self._get_config().values()
2724

2825
resp = requests.post(
29-
"%s/v2/captcha" % url,
26+
f"{url}/v2/captcha",
3027
json={
3128
"level": level,
3229
"media": media,
33-
"input_type": "text",
30+
"input_type": input_type,
3431
"size": "350x100",
3532
},
3633
)
3734

3835
if resp.ok:
3936
return resp.json()["id"]
4037

41-
error = "librecaptcha failed with code %s: %s" % (resp.status_code, resp.text)
38+
error = "captcha failed with code %s: %s" % (resp.status_code, resp.text)
4239
_logger.error(error)
43-
4440
raise Exception(error)
4541

4642
def media(self, captcha_id):
47-
url = self.env["ir.config_parameter"].sudo().get_param("base_librecaptcha.url")
43+
url = self._get_config().get("url")
4844

4945
resp = requests.get(
50-
"%s/v2/media" % url,
46+
f"{url}/v2/media",
5147
params={
5248
"id": captcha_id,
5349
},
@@ -57,25 +53,45 @@ def media(self, captcha_id):
5753
return resp.content
5854

5955
def answer(self, captcha_id, answer, raise_exception=False):
60-
url = self.env["ir.config_parameter"].sudo().get_param("base_librecaptcha.url")
56+
url = self._get_config().get("url")
6157
resp = requests.post(
62-
"%s/v2/answer" % url, json={"id": captcha_id, "answer": answer}
58+
f"{url}/v2/answer",
59+
json={
60+
"id": captcha_id,
61+
"answer": answer,
62+
},
6363
)
6464

6565
if resp.ok:
6666
result = resp.json()["result"]
6767
if raise_exception:
6868
if result == "False":
69-
raise ValidationError(_("Captcha incorrect."))
69+
raise UserError(_("Captcha incorrect."))
7070
if result == "Expired":
71-
raise ValidationError(_("Captcha Expired."))
71+
raise UserError(_("Captcha Expired."))
7272
return result
7373

7474
error = "librecaptcha failed with code %s: %s" % (resp.status_code, resp.text)
7575
_logger.error(error)
7676
raise Exception(error)
7777

7878
def is_enabled(self):
79-
return bool(
80-
self.env["ir.config_parameter"].sudo().get_param("base_librecaptcha.url")
81-
)
79+
record = self._get_config_record()
80+
return record.librecaptcha_enabled and record.librecaptcha_valid_server
81+
82+
def _get_config_record(self):
83+
"""method to be inherit to change the config record"""
84+
return self.env.company
85+
86+
def _get_config(self):
87+
if not self.is_enabled():
88+
return {}
89+
90+
record = self._get_config_record()
91+
92+
return {
93+
"url": record.librecaptcha_url,
94+
"level": record.librecaptcha_level,
95+
"media": f"image/{record.librecaptcha_media}",
96+
"input_type": record.librecaptcha_type,
97+
}
+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import requests
2+
3+
from odoo import api, fields, models
4+
5+
6+
class ResCompany(models.Model):
7+
_inherit = "res.company"
8+
9+
librecaptcha_enabled = fields.Boolean()
10+
librecaptcha_url = fields.Char()
11+
librecaptcha_level = fields.Selection(
12+
[
13+
("easy", "Easy"),
14+
("medium", "Medium"),
15+
("hard", "Hard"),
16+
],
17+
default="hard",
18+
required=True,
19+
)
20+
librecaptcha_media = fields.Selection(
21+
[
22+
("png", "PNG"),
23+
("gif", "GIF"),
24+
],
25+
default="png",
26+
required=True,
27+
)
28+
librecaptcha_type = fields.Selection(
29+
[
30+
("text", "Text"),
31+
],
32+
default="text",
33+
required=True,
34+
)
35+
librecaptcha_valid_server = fields.Boolean(
36+
compute="_compute_librecaptcha_valid_server",
37+
store=True,
38+
)
39+
40+
@api.depends("librecaptcha_url")
41+
def _compute_librecaptcha_valid_server(self):
42+
for record in self:
43+
if not record.librecaptcha_url:
44+
record.librecaptcha_valid_server = False
45+
continue
46+
47+
try:
48+
response = requests.get(record.librecaptcha_url, timeout=2)
49+
record.librecaptcha_valid_server = response.status_code == 200
50+
except requests.exceptions.ConnectionError:
51+
record.librecaptcha_valid_server = False
52+
53+
@api.model
54+
def _cron_librecaptcha_check_server(self):
55+
self.search([])._compute_librecaptcha_valid_server()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from odoo import fields, models
2+
3+
4+
class ResConfigSettings(models.TransientModel):
5+
_inherit = "res.config.settings"
6+
7+
librecaptcha_enabled = fields.Boolean(
8+
related="company_id.librecaptcha_enabled",
9+
readonly=False,
10+
)
11+
librecaptcha_url = fields.Char(
12+
related="company_id.librecaptcha_url",
13+
readonly=False,
14+
)
15+
librecaptcha_level = fields.Selection(
16+
related="company_id.librecaptcha_level",
17+
readonly=False,
18+
)
19+
librecaptcha_media = fields.Selection(
20+
related="company_id.librecaptcha_media",
21+
readonly=False,
22+
)
23+
librecaptcha_type = fields.Selection(
24+
related="company_id.librecaptcha_type",
25+
readonly=False,
26+
)
27+
librecaptcha_valid_server = fields.Boolean(
28+
related="company_id.librecaptcha_valid_server",
29+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<odoo>
3+
4+
<template id="librecaptcha_signup" inherit_id="auth_signup.signup">
5+
<t t-call="auth_signup.fields" position="after">
6+
<div t-if="captcha_enabled" class="form-group mb-3">
7+
<t t-call="base_librecaptcha.widget" />
8+
</div>
9+
</t>
10+
</template>
11+
12+
<template id="librecaptcha_login" inherit_id="web.login">
13+
<xpath expr="//input[@name='password']/parent::div" position="after">
14+
<div t-if="captcha_enabled" class="form-group mb-3">
15+
<t t-call="base_librecaptcha.widget" />
16+
</div>
17+
</xpath>
18+
</template>
19+
20+
</odoo>
+5-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
<?xml version="1.0" encoding="UTF-8" ?>
1+
<?xml version="1.0" encoding="UTF-8"?>
22
<odoo>
3+
34
<template id="widget">
45
<div class="js_captcha">
56
<label t-attf-for="name or 'captcha_answer'" class="form-label">Are you a robot?</label>
67
<input type="hidden" t-att-name="name_id or 'captcha_id'" class="form-control" />
7-
<img src="" class="d-none d-block mb-2" />
8-
<input type="text" t-att-name="name_answer or 'captcha_answer'" class="form-control" style="width: 350px;" />
8+
<img src="" class="d-none d-block mb-2" style="max-width: 100%; height: auto;"/>
9+
<input type="text" t-att-name="name_answer or 'captcha_answer'" required="required" class="form-control" />
910
</div>
1011
</template>
12+
1113
</odoo>

0 commit comments

Comments
 (0)