social/mail_tracking_mailgun/controllers/main.py

102 lines
4.2 KiB
Python

# Copyright 2021 Tecnativa - Jairo Llopis
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import hashlib
import hmac
import logging
from datetime import datetime, timedelta
from werkzeug.exceptions import NotAcceptable
from odoo import _
from odoo.exceptions import ValidationError
from odoo.http import request, route
from ...mail_tracking.controllers import main
from ...web.controllers.main import ensure_db
_logger = logging.getLogger(__name__)
class MailTrackingController(main.MailTrackingController):
def _mail_tracking_mailgun_webhook_verify(self, timestamp, token, signature):
"""Avoid mailgun webhook attacks.
See https://documentation.mailgun.com/en/latest/user_manual.html#securing-webhooks
""" # noqa: E501
# Request cannot be old
processing_time = datetime.utcnow() - datetime.utcfromtimestamp(int(timestamp))
if not timedelta() < processing_time < timedelta(minutes=10):
raise ValidationError(_("Request is too old"))
# Avoid replay attacks
try:
processed_tokens = (
request.env.registry._mail_tracking_mailgun_processed_tokens
)
except AttributeError:
processed_tokens = (
request.env.registry._mail_tracking_mailgun_processed_tokens
) = set()
if token in processed_tokens:
raise ValidationError(_("Request was already processed"))
processed_tokens.add(token)
params = request.env["mail.tracking.email"]._mailgun_values()
# Assert signature
if not params.webhook_signing_key:
_logger.warning(
"Skipping webhook payload verification. "
"Set `mailgun.webhook_signing_key` config parameter to enable"
)
return
hmac_digest = hmac.new(
key=params.webhook_signing_key.encode(),
msg=("{}{}".format(timestamp, token)).encode(),
digestmod=hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(str(signature), str(hmac_digest)):
raise ValidationError(_("Wrong signature"))
@route(["/mail/tracking/mailgun/all"], auth="none", type="json", csrf=False)
def mail_tracking_mailgun_webhook(self):
"""Process webhooks from Mailgun."""
ensure_db()
# Verify and return 406 in case of failure, to avoid retries
# See https://documentation.mailgun.com/en/latest/user_manual.html#routes
try:
self._mail_tracking_mailgun_webhook_verify(
**request.jsonrequest["signature"]
)
except ValidationError as error:
raise NotAcceptable from error
# v13: Under certain circumstances setting the bounce to hard faild partner
# leads to a triggering of the property_pricelist recomputation. We need to set
# company, user and uid for the query build to work correctly:
# https://github.com
# /odoo/odoo/blob/13.0/odoo/addons/base/models/ir_property.py#L240
#
# The result is an inderect error that impedes the correct bounce flagging and
# the event record creation:
#
# > ERROR prod odoo.sql_db: bad query:
# >
# > SELECT substr(p.res_id, 13)::integer, r.id
# > FROM ir_property p
# > LEFT JOIN product_pricelist r ON substr(p.value_reference, 19)::integer=r.id
# > WHERE p.fields_id=2440
# > AND (p.company_id=false OR p.company_id IS NULL)
# > AND (p.res_id IN ('res.partner,45621') OR p.res_id IS NULL)
# > ORDER BY p.company_id NULLS FIRST
# >
# > ERROR: operator does not exist: integer = boolean
# > LINE 6: AND (p.company_id=false OR p.company_id ...
#
# As far as we could research, this doesn't happen in >v14. So this nasty glitch
# only needs a fix for this version.
request.env.user = request.env["res.users"].browse(1)
request.env.company = request.env["res.company"].search([], limit=1)
request.env.uid = 1
# Process event
request.env["mail.tracking.email"].sudo()._mailgun_event_process(
request.jsonrequest["event-data"], self._request_metadata(),
)