mirror of
https://github.com/bringout/oca-ocb-security.git
synced 2026-04-23 16:32:08 +02:00
Initial commit: Security packages
This commit is contained in:
commit
bb469e4763
1399 changed files with 278378 additions and 0 deletions
|
|
@ -0,0 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import ir_http
|
||||
from . import res_config_settings
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
# -*- 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'
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
recaptcha_public_key = fields.Char("Site Key", config_parameter='recaptcha_public_key', groups='base.group_system')
|
||||
recaptcha_private_key = fields.Char("Secret Key", config_parameter='recaptcha_private_key', groups='base.group_system')
|
||||
recaptcha_min_score = fields.Float(
|
||||
"Minimum score",
|
||||
config_parameter='recaptcha_min_score',
|
||||
groups='base.group_system',
|
||||
default="0.7",
|
||||
help="By default, should be one of 0.1, 0.3, 0.7, 0.9.\n1.0 is very likely a good interaction, 0.0 is very likely a bot"
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue