Skip to content

Commit ccd0f4e

Browse files
authored
feat(add-ons): configure full URL for Fedora Messaging (#20196)
Providing host and SSL flag is not enough in some situations, so allow to configure the full URL.
1 parent 6869659 commit ccd0f4e

9 files changed

Lines changed: 306 additions & 54 deletions

File tree

docs/changes.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Weblate 2026.7
5858

5959
.. rubric:: Compatibility
6060

61-
* :ref:`addon-weblate.fedora_messaging.publish` topics now include category path segments, making categorized same-named components distinguishable.
61+
* :ref:`addon-weblate.fedora_messaging.publish` topics now include category path segments, and broker settings are stored as an AMQP URL with existing host and SSL settings migrated automatically.
6262

6363
.. rubric:: Upgrading
6464

docs/security/threat-model.rst

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ Scope and intended use
8686
:doc:`/admin/management`)
8787
* - Machine translation and outbound integrations
8888
- Machine translation, avatars, status reporting, telemetry, error
89-
reporting, VCS hosts, CDN add-on
90-
- Outbound HTTP(S), provider APIs, logs
89+
reporting, VCS hosts, CDN add-on, Fedora Messaging add-on
90+
- Outbound HTTP(S), AMQP(S), provider APIs, logs
9191
- In scope for Weblate's enforcement of configured access and network
9292
restrictions. Provider behavior is out of scope. *(documented)* (source: :doc:`/admin/config`, :doc:`/admin/addons`)
9393
* - Add-ons
@@ -245,7 +245,8 @@ What Weblate does to its host:
245245

246246
* It opens outbound network connections for configured VCS, identity-provider,
247247
avatar, machine-translation, backup, status-reporting, telemetry,
248-
error-reporting, and add-on features.
248+
error-reporting, and add-on features such as outbound webhooks and Fedora
249+
Messaging AMQP delivery.
249250
*(documented)* (source: :doc:`/admin/config`, :doc:`/admin/code-hosting`,
250251
:doc:`/admin/backup`)
251252
* It runs VCS and backup-related helper commands as part of repository and
@@ -318,8 +319,9 @@ Build-time and configuration variants
318319
*(documented)*
319320
* - Private-target restrictions and allowlists for outbound URLs
320321
- User-configurable outbound URL surfaces documented with private-target
321-
restriction settings reject internal or non-public targets by default.
322-
*(documented)* (source: :setting:`ASSET_RESTRICT_PRIVATE`,
322+
restriction settings, including Fedora Messaging AMQP broker URLs,
323+
reject internal or non-public targets by default. *(documented)*
324+
(source: :setting:`ASSET_RESTRICT_PRIVATE`,
323325
:setting:`PROJECT_WEB_RESTRICT_PRIVATE`,
324326
:setting:`WEBHOOK_RESTRICT_PRIVATE`, :setting:`VCS_RESTRICT_PRIVATE`)
325327
- Allowlist settings and privileged configuration can intentionally expand
@@ -530,8 +532,9 @@ Security properties Weblate provides
530532
- Default private-target checks are enabled and no trusted allowlist
531533
exemption applies.
532534
- A user-configurable screenshot URL, remote HTML URL, project website or
533-
repository browser URL, outbound webhook URL, or VCS URL reaches an
534-
internal or non-public target despite default controls.
535+
repository browser URL, outbound webhook URL, Fedora Messaging AMQP
536+
broker URL, or VCS URL reaches an internal or non-public target despite
537+
default controls.
535538
- Security-critical when it exposes internal services or metadata.
536539
* - Weblate records security-relevant account, permission, and project or
537540
component setting changes in audit logs or history. *(documented)*

docs/snippets/addons-autogenerated.rst

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -433,22 +433,20 @@ Fedora Messaging
433433
.. versionadded:: 5.15
434434

435435
:Add-on ID: ``weblate.fedora_messaging.publish``
436-
:Configuration: +------------------+-----------------------------+-------------------------------------------------------------------------------------------------+
437-
| ``amqp_host`` | AMQP broker host | The AMQP broker to connect to. |
438-
+------------------+-----------------------------+-------------------------------------------------------------------------------------------------+
439-
| ``amqp_ssl`` | Use SSL for AMQP connection | |
440-
+------------------+-----------------------------+-------------------------------------------------------------------------------------------------+
441-
| ``ca_cert`` | CA certificates | Bundle of PEM encoded CA certificates used to validate the certificate presented by the server. |
442-
+------------------+-----------------------------+-------------------------------------------------------------------------------------------------+
443-
| ``client_key`` | Client SSL key | PEM encoded client private SSL key. |
444-
+------------------+-----------------------------+-------------------------------------------------------------------------------------------------+
445-
| ``client_cert`` | Client SSL certificates | PEM encoded client SSL certificate. |
446-
+------------------+-----------------------------+-------------------------------------------------------------------------------------------------+
447-
| ``event_filter`` | Change events to trigger | Choose which change events should trigger this add-on. |
448-
| | | :ref:`addon-choice-event_filter` |
449-
+------------------+-----------------------------+-------------------------------------------------------------------------------------------------+
450-
| ``events`` | Selected change events | :ref:`addon-choice-events` |
451-
+------------------+-----------------------------+-------------------------------------------------------------------------------------------------+
436+
:Configuration: +------------------+--------------------------+-------------------------------------------------------------------------------------------------+
437+
| ``amqp_url`` | AMQP broker URL | The AMQP broker URL to connect to. |
438+
+------------------+--------------------------+-------------------------------------------------------------------------------------------------+
439+
| ``ca_cert`` | CA certificates | Bundle of PEM encoded CA certificates used to validate the certificate presented by the server. |
440+
+------------------+--------------------------+-------------------------------------------------------------------------------------------------+
441+
| ``client_key`` | Client SSL key | PEM encoded client private SSL key. |
442+
+------------------+--------------------------+-------------------------------------------------------------------------------------------------+
443+
| ``client_cert`` | Client SSL certificates | PEM encoded client SSL certificate. |
444+
+------------------+--------------------------+-------------------------------------------------------------------------------------------------+
445+
| ``event_filter`` | Change events to trigger | Choose which change events should trigger this add-on. |
446+
| | | :ref:`addon-choice-event_filter` |
447+
+------------------+--------------------------+-------------------------------------------------------------------------------------------------+
448+
| ``events`` | Selected change events | :ref:`addon-choice-events` |
449+
+------------------+--------------------------+-------------------------------------------------------------------------------------------------+
452450

453451
:Triggers: :ref:`addon-event-event-change`
454452

weblate/addons/fedora_messaging.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ def change_event(
6868

6969
# Apply configuration
7070
self.configure_fedora_messaging(
71-
amqp_host=config["amqp_host"],
72-
amqp_ssl=config.get("amqp_ssl", False),
71+
amqp_url=config["amqp_url"],
7372
ca_cert=config.get("ca_cert"),
7473
client_key=config.get("client_key"),
7574
client_cert=config.get("client_cert"),
@@ -149,17 +148,13 @@ def get_change_headers(change: Change) -> dict[str, str]:
149148
@staticmethod
150149
def configure_fedora_messaging(
151150
*,
152-
amqp_host: str,
153-
amqp_ssl: bool,
151+
amqp_url: str,
154152
ca_cert: str | None,
155153
client_key: str | None,
156154
client_cert: str | None,
157155
force_update: bool = False,
158156
) -> None:
159157
"""Configure Fedora Messaging."""
160-
# Build AMQP URL, the parameters might be configurable
161-
amqp_url = f"{'amqps' if amqp_ssl else 'amqp'}://{amqp_host}?connection_attempts=3&retry_delay=5"
162-
163158
# Hash certificates to detect configuration changes
164159
cert_hash = siphash(
165160
"Fedora Messaging", f"CA:{ca_cert},KEY:{client_key},CERT:{client_cert}"

weblate/addons/forms.py

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from pathlib import Path
88
from typing import TYPE_CHECKING, Any, ClassVar, cast
9+
from urllib.parse import urlparse
910

1011
import regex
1112
from crispy_forms.helper import FormHelper
@@ -43,7 +44,7 @@
4344
from weblate.utils.regex import compile_regex, regex_match, regex_sub
4445
from weblate.utils.render import validate_render, validate_render_translation
4546
from weblate.utils.validators import (
46-
DomainOrIPValidator,
47+
validate_fedora_messaging_url,
4748
validate_filename,
4849
validate_re,
4950
validate_re_nonempty,
@@ -1616,14 +1617,10 @@ class WebhooksAddonForm(BaseWebhooksAddonForm):
16161617

16171618

16181619
class FedoraMessagingAddonForm(ChangeBaseAddonForm):
1619-
amqp_host = forms.CharField(
1620-
label=gettext_lazy("AMQP broker host"),
1621-
help_text=gettext_lazy("The AMQP broker to connect to."),
1622-
validators=[DomainOrIPValidator()],
1623-
)
1624-
amqp_ssl = forms.BooleanField(
1625-
label=gettext_lazy("Use SSL for AMQP connection"),
1626-
required=False,
1620+
amqp_url = forms.CharField(
1621+
label=gettext_lazy("AMQP broker URL"),
1622+
help_text=gettext_lazy("The AMQP broker URL to connect to."),
1623+
validators=[validate_fedora_messaging_url],
16271624
)
16281625
ca_cert = forms.CharField(
16291626
widget=forms.Textarea(),
@@ -1648,8 +1645,7 @@ class FedoraMessagingAddonForm(ChangeBaseAddonForm):
16481645
)
16491646

16501647
field_order = [ # ruff: ignore[mutable-class-default]
1651-
"amqp_host",
1652-
"amqp_ssl",
1648+
"amqp_url",
16531649
"ca_cert",
16541650
"client_key",
16551651
"client_cert",
@@ -1663,17 +1659,17 @@ def clean(self) -> None:
16631659
FedoraMessagingAddon,
16641660
)
16651661

1666-
amqp_ssl = self.cleaned_data.get("amqp_ssl")
1667-
if amqp_ssl is not None:
1668-
if amqp_ssl:
1662+
amqp_url = self.cleaned_data.get("amqp_url")
1663+
if amqp_url:
1664+
if urlparse(amqp_url).scheme == "amqps":
16691665
if (
16701666
not self.cleaned_data.get("ca_cert")
16711667
or not self.cleaned_data.get("client_key")
16721668
or not self.cleaned_data.get("client_cert")
16731669
):
16741670
raise forms.ValidationError(
16751671
{
1676-
"amqp_ssl": gettext(
1672+
"amqp_url": gettext(
16771673
"The SSL certificates have to be provided for SSL connection."
16781674
)
16791675
}
@@ -1686,17 +1682,16 @@ def clean(self) -> None:
16861682
):
16871683
raise forms.ValidationError(
16881684
{
1689-
"amqp_ssl": gettext(
1685+
"amqp_url": gettext(
16901686
"The SSL certificates are not used without a SSL connection."
16911687
)
16921688
}
16931689
)
16941690

1695-
if amqp_host := self.cleaned_data.get("amqp_host"):
1691+
if amqp_url:
16961692
try:
16971693
FedoraMessagingAddon.configure_fedora_messaging(
1698-
amqp_host=amqp_host,
1699-
amqp_ssl=self.cleaned_data.get("amqp_ssl", False),
1694+
amqp_url=amqp_url,
17001695
ca_cert=self.cleaned_data.get("ca_cert"),
17011696
client_key=self.cleaned_data.get("client_key"),
17021697
client_cert=self.cleaned_data.get("client_cert"),
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright © Michal Čihař <michal@weblate.org>
2+
#
3+
# SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
from __future__ import annotations
6+
7+
from django.db import migrations
8+
9+
FEDORA_MESSAGING_ADDON = "weblate.fedora_messaging.publish"
10+
11+
12+
def migrate_fedora_messaging_amqp_url(apps, _schema_editor) -> None:
13+
Addon = apps.get_model("addons", "Addon")
14+
15+
for addon in Addon.objects.filter(name=FEDORA_MESSAGING_ADDON):
16+
configuration = dict(addon.configuration or {})
17+
original = configuration.copy()
18+
19+
if "amqp_url" not in configuration:
20+
amqp_host = configuration.get("amqp_host")
21+
if amqp_host:
22+
amqp_scheme = "amqps" if configuration.get("amqp_ssl") else "amqp"
23+
configuration["amqp_url"] = (
24+
f"{amqp_scheme}://{amqp_host}?connection_attempts=3&retry_delay=5"
25+
)
26+
27+
configuration.pop("amqp_host", None)
28+
configuration.pop("amqp_ssl", None)
29+
30+
if configuration != original:
31+
Addon.objects.filter(pk=addon.pk).update(configuration=configuration)
32+
33+
34+
class Migration(migrations.Migration):
35+
dependencies = [
36+
("addons", "0020_remove_obsolete_cleanup_tasks"),
37+
]
38+
39+
operations = [
40+
migrations.RunPython(
41+
migrate_fedora_messaging_amqp_url,
42+
migrations.RunPython.noop,
43+
),
44+
]

0 commit comments

Comments
 (0)