summaryrefslogtreecommitdiff
path: root/addons/google_recaptcha/models/ir_http.py
diff options
context:
space:
mode:
Diffstat (limited to 'addons/google_recaptcha/models/ir_http.py')
-rw-r--r--addons/google_recaptcha/models/ir_http.py96
1 files changed, 96 insertions, 0 deletions
diff --git a/addons/google_recaptcha/models/ir_http.py b/addons/google_recaptcha/models/ir_http.py
new file mode 100644
index 00000000..f0306cb1
--- /dev/null
+++ b/addons/google_recaptcha/models/ir_http.py
@@ -0,0 +1,96 @@
+# -*- 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'
+
+ @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'