Skip to content

Commit

Permalink
Merge pull request #3193 from digitalfabrik/feature/zammad-webhook-token
Browse files Browse the repository at this point in the history
Zammad: remove unique URL & add webhook token
  • Loading branch information
svenseeberg authored Nov 19, 2024
2 parents dff3299 + 0286faf commit 2126d07
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 21 deletions.
8 changes: 4 additions & 4 deletions integreat_cms/api/v3/chat/user_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import json
import logging
import random
import socket
from typing import TYPE_CHECKING

from django.http import HttpResponse, JsonResponse
Expand Down Expand Up @@ -227,10 +226,11 @@ def zammad_webhook(request: HttpRequest) -> JsonResponse:
"""
Receive webhooks from Zammad to update the latest article translation
"""
zammad_url = (
f"https://{socket.getnameinfo((request.META.get('REMOTE_ADDR'), 0), 0)[0]}"
region = get_object_or_404(
Region, zammad_webhook_token=request.GET.get("token", None)
)
region = get_object_or_404(Region, zammad_url=zammad_url)
if not region.integreat_chat_enabled:
return JsonResponse({"status": "Integreat Chat disabled"})
client = ZammadChatAPI(region)
webhook_message = json.loads(request.body)
message_text = webhook_message["article"]["body"]
Expand Down
22 changes: 15 additions & 7 deletions integreat_cms/cms/fixtures/test_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
"integreat_chat_enabled": true,
"zammad_url": "https://zammad.example.com",
"zammad_access_token": "dummytoken",
"zammad_webhook_token": "07cce1d7-ec79-4ae3-98cf-04d340ae6655",
"zammad_chat_handlers": "[email protected]",
"chat_beta_tester_percentage": 50,
"offers": [3, 2, 1, 5]
Expand Down Expand Up @@ -194,8 +195,9 @@
"machine_translate_events": 1,
"machine_translate_pois": 1,
"integreat_chat_enabled": false,
"zammad_url": null,
"zammad_url": "",
"zammad_access_token": "",
"zammad_webhook_token": "eaea8b88-5f17-4d55-b04c-face1264ca45",
"zammad_chat_handlers": "",
"chat_beta_tester_percentage": 0,
"offers": [3, 4, 2]
Expand Down Expand Up @@ -247,8 +249,9 @@
"machine_translate_events": 1,
"machine_translate_pois": 1,
"integreat_chat_enabled": false,
"zammad_url": null,
"zammad_url": "",
"zammad_access_token": "",
"zammad_webhook_token": "bf21d6b8-0dcf-4ac9-ba00-1befac1ee25a",
"zammad_chat_handlers": "",
"chat_beta_tester_percentage": 0,
"offers": []
Expand Down Expand Up @@ -300,8 +303,9 @@
"machine_translate_events": 1,
"machine_translate_pois": 1,
"integreat_chat_enabled": false,
"zammad_url": null,
"zammad_url": "",
"zammad_access_token": "",
"zammad_webhook_token": "4f130b2e-b85e-4a0e-8a31-63591adb6b3c",
"zammad_chat_handlers": "",
"chat_beta_tester_percentage": 0,
"offers": []
Expand Down Expand Up @@ -353,8 +357,9 @@
"machine_translate_events": 1,
"machine_translate_pois": 1,
"integreat_chat_enabled": false,
"zammad_url": null,
"zammad_url": "",
"zammad_access_token": "",
"zammad_webhook_token": "9de4a0be-c394-44a3-8e35-434c5325d8af",
"zammad_chat_handlers": "",
"chat_beta_tester_percentage": 0,
"offers": [1]
Expand Down Expand Up @@ -406,8 +411,9 @@
"machine_translate_events": 1,
"machine_translate_pois": 1,
"integreat_chat_enabled": false,
"zammad_url": null,
"zammad_url": "",
"zammad_access_token": "",
"zammad_webhook_token": "249bdf60-511d-45a4-a4df-d3b6feca26a1",
"zammad_chat_handlers": "",
"chat_beta_tester_percentage": 0,
"offers": []
Expand Down Expand Up @@ -459,8 +465,9 @@
"machine_translate_events": 1,
"machine_translate_pois": 1,
"integreat_chat_enabled": false,
"zammad_url": null,
"zammad_url": "",
"zammad_access_token": "",
"zammad_webhook_token": "ef0a7824-1516-45e2-b878-e1ad5a34b70b",
"zammad_chat_handlers": "",
"chat_beta_tester_percentage": 0,
"offers": []
Expand Down Expand Up @@ -512,8 +519,9 @@
"machine_translate_events": 1,
"machine_translate_pois": 1,
"integreat_chat_enabled": false,
"zammad_url": null,
"zammad_url": "",
"zammad_access_token": "",
"zammad_webhook_token": "1a6940d4-9114-470a-9077-42db05037511",
"zammad_chat_handlers": "",
"chat_beta_tester_percentage": 0,
"offers": []
Expand Down
11 changes: 7 additions & 4 deletions integreat_cms/cms/forms/regions/region_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ class Meta:
"integreat_chat_enabled",
"zammad_url",
"zammad_access_token",
"zammad_webhook_token",
"zammad_chat_handlers",
"chat_beta_tester_percentage",
]
Expand Down Expand Up @@ -397,12 +398,14 @@ def clean(self) -> dict[str, Any]:

# Integreat Chat can only be enabled if Zammad URL and access key are set
if cleaned_data["integreat_chat_enabled"] and (
not cleaned_data["zammad_url"] or not cleaned_data["zammad_access_token"]
not cleaned_data["zammad_url"]
or not cleaned_data["zammad_access_token"]
or not cleaned_data["zammad_webhook_token"]
):
self.add_error(
"integreat_chat_enabled",
_(
"A Zammad URL and Access Token are required in order to enable the Integreat Chat."
"A Zammad URL, Zammad Webhook Token and Access Token are required in order to enable the Integreat Chat."
),
)

Expand Down Expand Up @@ -607,14 +610,14 @@ def clean_hix_enabled(self) -> bool:
)
return cleaned_hix_enabled

def clean_zammad_url(self) -> str | None:
def clean_zammad_url(self) -> str:
"""
Validate the zammad_url field (see :ref:`overriding-modelform-clean-method`).
:return: The validated field
"""
if not (cleaned_zammad_url := self.cleaned_data["zammad_url"]):
return None
return ""
# Remove superfluous path parts
cleaned_zammad_url = cleaned_zammad_url.split("/api/v1")[0]
cleaned_zammad_url = cleaned_zammad_url.rstrip("/")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Generated by Django 4.2.13 on 2024-11-08 12:41

import uuid
from typing import Callable

from django.apps.registry import Apps
from django.db import migrations, models


def set_zammad_urls(
apps: Apps, schema_editor: Callable # pylint: disable=unused-argument
) -> None:
"""
Update empty Zammad URLs to Null
"""
Region = apps.get_model("cms", "Region")
Region.objects.filter(zammad_url=None).update(zammad_url="")
for region in Region.objects.all():
region.zammad_webhook_token = uuid.uuid4()
region.save()


class Migration(migrations.Migration):
"""
Remove zammad_url unique constraint, replace Null values with empty strings, remove null=true
"""

dependencies = [
("cms", "0109_custom_truncating_char_field"),
]

operations = [
migrations.AddField(
model_name="region",
name="zammad_webhook_token",
field=models.UUIDField(
blank=True,
default=uuid.uuid4,
help_text="Token used by Zammad webhooks to inform the Integreat CMS about changed tickets. The token has to be appended with a token= GET parameter to the webhook path.",
verbose_name="Token used by Zammad webhook",
),
),
migrations.AlterField(
model_name="region",
name="zammad_url",
field=models.URLField(
blank=True,
default=None,
help_text="URL pointing to this region's Zammad instance. Setting this enables Zammad form offers.",
max_length=256,
null=True,
verbose_name="Zammad-URL",
),
),
migrations.RunPython(set_zammad_urls),
migrations.AlterField(
model_name="region",
name="zammad_url",
field=models.URLField(
blank=True,
default="",
help_text="URL pointing to this region's Zammad instance. Setting this enables Zammad form offers.",
max_length=256,
verbose_name="Zammad-URL",
),
),
]
14 changes: 11 additions & 3 deletions integreat_cms/cms/models/regions/region.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import logging
import uuid
from datetime import datetime
from html import escape
from typing import TYPE_CHECKING
Expand Down Expand Up @@ -418,9 +419,7 @@ class Region(AbstractBaseModel):
zammad_url = models.URLField(
max_length=256,
blank=True,
null=True,
default=None,
unique=True,
default="",
verbose_name=_("Zammad-URL"),
help_text=_(
"URL pointing to this region's Zammad instance. Setting this enables Zammad form offers."
Expand All @@ -435,6 +434,15 @@ class Region(AbstractBaseModel):
'Access token for a Zammad user account. In Zammad, the account must be part of the "Agent" role and have full group permissions for the group:'
),
)
zammad_webhook_token = models.UUIDField(
max_length=64,
blank=True,
default=uuid.uuid4,
verbose_name=_("Token used by Zammad webhook"),
help_text=_(
"Token used by Zammad webhooks to inform the Integreat CMS about changed tickets. The token has to be appended with a token= GET parameter to the webhook path."
),
)
zammad_chat_handlers = models.CharField(
max_length=1024,
blank=True,
Expand Down
7 changes: 7 additions & 0 deletions integreat_cms/cms/templates/regions/region_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,13 @@ <h3 class="heading font-bold text-black">
<div class="help-text">
{{ form.zammad_url.help_text }}
</div>
<label for="{{ form.zammad_webhook_token.id_for_label }}">
{{ form.zammad_webhook_token.label }}
</label>
{% render_field form.zammad_webhook_token %}
<div class="help-text">
{{ form.zammad_webhook_token.help_text }}
</div>
<label for="{{ form.zammad_access_token.id_for_label }}">
{{ form.zammad_access_token.label }}
</label>
Expand Down
20 changes: 17 additions & 3 deletions integreat_cms/locale/de/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -2333,10 +2333,10 @@ msgstr ""

#: cms/forms/regions/region_form.py
msgid ""
"A Zammad URL and Access Token are required in order to enable the Integreat "
"Chat."
"A Zammad URL, Zammad Webhook Token and Access Token are required in order to "
"enable the Integreat Chat."
msgstr ""
"Eine Zammad URL und ein Zugangstoken sind erforderlich um, den Integreat "
"Eine Zammad URL, ein Zammad Webhook Token und ein Zugangstoken sind erforderlich um, den Integreat "
"Chat aktivieren."

#: cms/forms/regions/region_form.py
Expand Down Expand Up @@ -4224,6 +4224,20 @@ msgstr ""
"Teil der \"Agent\" Rolle sein und volle Gruppenberechtigungen für folgende "
"Gruppe haben:"

#: cms/models/regions/region.py
msgid "Token used by Zammad webhook"
msgstr "Von Zammad Webhooks verwendeter Token"

#: cms/models/regions/region.py
msgid ""
"Token used by Zammad webhooks to inform the Integreat CMS about changed "
"tickets. The token has to be appended with a token= GET parameter to the "
"webhook path."
msgstr ""
"Token der von Zammad Webhooks verwendet wird, um das Integreat CMS über "
"geänderte Tickets zu informieren. Der Token muss als token= GET parameter dem "
"Webhook-Pfad angefügt werden."

#: cms/models/regions/region.py
msgid "Zammad chat handlers"
msgstr "Zammad Chat-Zuständige"
Expand Down
2 changes: 2 additions & 0 deletions integreat_cms/release_notes/current/unreleased/3192.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
en: Replace unique Zammad URL constraint with Zammad webhook token
de: Ersetze eindeutige Zammad-URL-Bedingung durch Zammad Webhook Token

0 comments on commit 2126d07

Please sign in to comment.