Files
odoo_source/addons/google_recaptcha/models/ir_http.py
T
Julien Mougenot 5cffff6e60 [FIX] google_recaptcha: Add key in session_info
Before this commit, if there was a Google ReCaptcha public key
registered on the database, it was given to the backend along with
the assets in the `session_info` global key.

Since this key is meant to be used on the frontend to generate recaptcha
tokens, it should be available there as well.

Now, it is also given to the frontend version of the `session_info` key.

closes odoo/odoo#76600

Task: 2639095
X-original-commit: 27136cd09abfb4e012b0df2d53877534cbcdad28
Signed-off-by: Julien Mougenot (jum) <jum@odoo.com>
2021-09-15 17:50:41 +00:00

114 lines
5.3 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import requests
from odoo import api, models, _
from odoo.http import request
from odoo.exceptions import UserError, ValidationError
logger = logging.getLogger(__name__)
class Http(models.AbstractModel):
_inherit = 'ir.http'
def session_info(self):
session_info = super().session_info()
return self._add_public_key_to_session_info(session_info)
@api.model
def get_frontend_session_info(self):
frontend_session_info = super().get_frontend_session_info()
return self._add_public_key_to_session_info(frontend_session_info)
@api.model
def _add_public_key_to_session_info(self, session_info):
"""Add the ReCaptcha public key to the given session_info object"""
public_key = self.env['ir.config_parameter'].sudo().get_param('recaptcha_public_key')
if public_key:
session_info['recaptcha_public_key'] = public_key
return session_info
@api.model
def _verify_request_recaptcha_token(self, action):
""" Verify the recaptcha token for the current request.
If no recaptcha private key is set the recaptcha verification
is considered inactive and this method will return True.
"""
ip_addr = request.httprequest.remote_addr
token = request.params.pop('recaptcha_token_response', False)
recaptcha_result = request.env['ir.http']._verify_recaptcha_token(ip_addr, token, action)
if recaptcha_result in ['is_human', 'no_secret']:
return True
if recaptcha_result == 'wrong_secret':
raise ValidationError(_("The reCaptcha private key is invalid."))
elif recaptcha_result == 'wrong_token':
raise ValidationError(_("The reCaptcha token is invalid."))
elif recaptcha_result == 'timeout':
raise UserError(_("Your request has timed out, please retry."))
elif recaptcha_result == 'bad_request':
raise UserError(_("The request is invalid or malformed."))
else:
return False
@api.model
def _verify_recaptcha_token(self, ip_addr, token, action=False):
"""
Verify a recaptchaV3 token and returns the result as a string.
RecaptchaV3 verify DOC: https://developers.google.com/recaptcha/docs/verify
:return: The result of the call to the google API:
is_human: The token is valid and the user trustworthy.
is_bot: The user is not trustworthy and most likely a bot.
no_secret: No reCaptcha secret set in settings.
wrong_action: the action performed to obtain the token does not match the one we are verifying.
wrong_token: The token provided is invalid or empty.
wrong_secret: The private key provided in settings is invalid.
timeout: The request has timout or the token provided is too old.
bad_request: The request is invalid or malformed.
:rtype: str
"""
private_key = request.env['ir.config_parameter'].sudo().get_param('recaptcha_private_key')
if not private_key:
return 'no_secret'
min_score = request.env['ir.config_parameter'].sudo().get_param('recaptcha_min_score')
try:
r = requests.post('https://www.recaptcha.net/recaptcha/api/siteverify', {
'secret': private_key,
'response': token,
'remoteip': ip_addr,
}, timeout=2) # it takes ~50ms to retrieve the response
result = r.json()
res_success = result['success']
res_action = res_success and action and result['action']
except requests.exceptions.Timeout:
logger.error("Trial captcha verification timeout for ip address %s", ip_addr)
return 'timeout'
except Exception:
logger.error("Trial captcha verification bad request response")
return 'bad_request'
if res_success:
score = result.get('score', False)
if score < float(min_score):
logger.warning("Trial captcha verification for ip address %s failed with score %f.", ip_addr, score)
return 'is_bot'
if res_action and res_action != action:
logger.warning("Trial captcha verification for ip address %s failed with action %f, expected: %s.", ip_addr, score, action)
return 'wrong_action'
logger.info("Trial captcha verification for ip address %s succeeded with score %f.", ip_addr, score)
return 'is_human'
errors = result.get('error-codes', [])
logger.warning("Trial captcha verification for ip address %s failed error codes %r. token was: [%s]", ip_addr, errors, token)
for error in errors:
if error in ['missing-input-secret', 'invalid-input-secret']:
return 'wrong_secret'
if error in ['missing-input-response', 'invalid-input-response']:
return 'wrong_token'
if error == 'timeout-or-duplicate':
return 'timeout'
if error == 'bad-request':
return 'bad_request'
return 'is_bot'