diff --git a/scipost_django/SciPost_v1/settings/base.py b/scipost_django/SciPost_v1/settings/base.py index 24da99a92d190b7f638d335f17c79dba2edb85a7..e3b4407f46520c28ead75fc753950e531e538dc8 100644 --- a/scipost_django/SciPost_v1/settings/base.py +++ b/scipost_django/SciPost_v1/settings/base.py @@ -632,3 +632,7 @@ GITLAB_KEY = get_secret("GITLAB_KEY") # ORCID API ORCID_CLIENT_ID = get_secret("ORCID_CLIENT_ID") ORCID_CLIENT_SECRET = get_secret("ORCID_CLIENT_SECRET") + + +# SLACK API +SLACK_WEBHOOK_URL_MAILGUN_ALERTS = get_secret("SLACK_WEBHOOK_URL_MAILGUN_ALERTS") diff --git a/scipost_django/apimail/api/views/__init__.py b/scipost_django/apimail/api/views/__init__.py index a0aa6dfa02b41eceb4842ac207d0fd3168e2d0a8..49109cb7ca0f9c972f61af74088ca9f6d56f7081 100644 --- a/scipost_django/apimail/api/views/__init__.py +++ b/scipost_django/apimail/api/views/__init__.py @@ -25,3 +25,5 @@ from .stored_message import ( StoredMessageUpdateReadAPIView, StoredMessageUpdateTagAPIView, ) + +from .integrations import mailgun_webhook diff --git a/scipost_django/apimail/api/views/integrations.py b/scipost_django/apimail/api/views/integrations.py new file mode 100644 index 0000000000000000000000000000000000000000..2918d96de14b648418096b19e13b9314ed342f90 --- /dev/null +++ b/scipost_django/apimail/api/views/integrations.py @@ -0,0 +1,71 @@ +__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" +__license__ = "AGPL v3" + +import json +from django.conf import settings +from django.http import HttpResponse +from django.views.decorators.csrf import csrf_exempt + + +def mailgun_webhook_is_signed(timestamp, token, signature): + """ + Verify the signature of a mailgun webhook. + """ + import hmac + + encoded_token = hmac.new( + settings.MAILGUN_API_KEY.encode(), + (timestamp + token).encode(), + "sha256", + ).hexdigest() + + return encoded_token == signature + + +def send_mailgun_alert_slack_message(event_data: dict): + """ + Format mailgun event data and send an alert to Slack + """ + import requests + + event = event_data.get("event", "unknown") + reason = event_data.get("reason", "unknown") + recipient = event_data.get("message", {}).get("headers", {}).get("to", "unknown") + sender = event_data.get("message", {}).get("headers", {}).get("from", "unknown") + subject = event_data.get("message", {}).get("headers", {}).get("subject", "unknown") + + message = f"[{event.upper()}] {recipient} -> {sender}\nSubject: {subject}\nReason: {reason}" + + response = requests.post( + settings.SLACK_WEBHOOK_URL_MAILGUN_ALERTS, + json={"text": message}, + headers={"Content-type": "application/json"}, + ) + + return response + + +@csrf_exempt +def mailgun_webhook(request): + """ + Endpoint to receive POST requests for mailgun webhook. + Executes custom integrations upon reception. + """ + + if request.method != "POST": + return HttpResponse(status=405) + + data = json.loads(request.body) + + if "signature" not in data: + return HttpResponse(status=400) + + # Verify signature, return 403 if invalid + if not mailgun_webhook_is_signed(**data.get("signature")): + return HttpResponse(status=403) + + # Apply custom integrations here + event_data = data.get("event-data", {}) + send_mailgun_alert_slack_message(event_data) + + return HttpResponse(status=200) diff --git a/scipost_django/apimail/urls.py b/scipost_django/apimail/urls.py index 725f95ba8212eebe9964e4e5bd8beafe9b985975..f4846154a87405604bdb2c247017fa1873bc9e7a 100644 --- a/scipost_django/apimail/urls.py +++ b/scipost_django/apimail/urls.py @@ -117,5 +117,6 @@ urlpatterns = [ path( # /mail/attachment_file/<uuid> "attachment_file/<uuid:uuid>", views.attachment_file, name="attachment_file" ), + path("mailgun_webhook", apiviews.mailgun_webhook, name="mailgun_webhook"), path("", views.mail, name="mail"), # /mail ]