Optimized safe options

This commit is contained in:
Kevin Veen-Birkenbach 2025-05-17 13:39:57 +02:00
parent 2506065142
commit ad51597e2e
No known key found for this signature in database
GPG Key ID: 44D8F11FD62F878E
5 changed files with 98 additions and 14 deletions

View File

@ -0,0 +1,29 @@
# file: filter_plugins/safe_join.py
"""
Ansible filter plugin that joins a base string and a tail path safely.
If the base is falsy (None, empty, etc.), returns an empty string.
"""
def safe_join(base, tail):
"""
Safely join base and tail into a path or URL.
- base: the base string. If falsy, returns ''.
- tail: the string to append. Leading/trailing slashes are handled.
- On any exception, returns ''.
"""
try:
if not base:
return ''
base_str = str(base).rstrip('/')
tail_str = str(tail).lstrip('/')
return f"{base_str}/{tail_str}"
except Exception:
return ''
class FilterModule(object):
def filters(self):
return {
'safe_join': safe_join,
}

View File

@ -9,18 +9,12 @@ defaults_service_provider:
city: "Cybertown"
postal_code: "00001"
country: "Nexusland"
logo: >-
{{ (applications.assets_server.url | safe_var ~ '/logo.png')
if applications.assets_server.url | safe_var else '' }}
logo: "{{ applications.assets_server.url | safe_var | safe_join('logo.png') }}"
platform:
titel: "CyMaIS Demo"
subtitel: "The Future of Self-Hosted Infrastructure. Secure. Automated. Sovereign."
logo: >-
{{ (applications.assets_server.url | safe_var ~ '/logo.png')
if applications.assets_server.url | safe_var else '' }}
favicon: >-
{{ (applications.assets_server.url | safe_var ~ '/favicon.ico')
if applications.assets_server.url | safe_var else '' }}
logo: "{{ applications.assets_server.url | safe_var | safe_join('logo.png') }}"
favicon: "{{ applications.assets_server.url | safe_var | safe_join('favicon.ico') }}"
contact:
bluesky: >-
{{ ('@' ~ users.administrator.username ~ '.' ~ domains[application_id]['api'])
@ -36,4 +30,10 @@ defaults_service_provider:
legal:
editorial_responsible: "Johannes Gutenberg"
source_code: "https://github.com/kevinveenbirkenbach/cymais"
imprint: "{{ web_protocol }}://{{domains.html_server}}/imprint.html"
imprint: >-
{{ "{protocol}://{domain}/imprint.html"
| safe_placeholders({
'protocol': web_protocol,
'domain': domains.html_server
})
}}

View File

@ -51,8 +51,10 @@ env:
DISCOURSE_DEVELOPER_EMAILS: {{users.administrator.email}}
# Set Logo
{% if service_provider.platform.logo | bool %}
DISCOURSE_LOGO_URL: "{{ service_provider.platform.logo }}"
DISCOURSE_LOGO_SMALL_URL: "{{ service_provider.platform.logo }}"
{% endif %}
# SMTP ADDRESS, username, and password are required
# WARNING the char '#' in SMTP password can cause problems!

View File

@ -65,8 +65,9 @@ listmonk_settings:
# - key: "messengers"
# value: '[]'
- key: "app.logo_url"
value: '"{{ service_provider.platform.logo }}"'
- key: "app.logo_url"
value: '"{{ service_provider.platform.logo }}"'
when: service_provider.platform.logo | bool
- key: "app.site_name"
value: '"{{ service_provider.company.titel }} Mailing list"'
@ -115,8 +116,9 @@ listmonk_settings:
# - key: "app.concurrency"
# value: '10'
- key: "app.favicon_url"
value: '"{{ service_provider.platform.favicon }}"'
- key: "app.favicon_url"
value: '"{{ service_provider.platform.favicon }}"'
when: service_provider.platform.favicon | bool
# - key: "bounce.postmark"
# value: '{"enabled": false, "password": "", "username": ""}'

View File

@ -0,0 +1,51 @@
import unittest
import sys
import os
# Ensure filter_plugins directory is on the path
sys.path.insert(
0,
os.path.abspath(os.path.join(os.path.dirname(__file__), '../../filter_plugins'))
)
from safe_join import safe_join
class TestSafeJoinFilter(unittest.TestCase):
def test_join_with_trailing_slashes(self):
self.assertEqual(
safe_join('http://example.com/', '/path/to'),
'http://example.com/path/to'
)
def test_join_without_slashes(self):
self.assertEqual(
safe_join('http://example.com', 'path/to'),
'http://example.com/path/to'
)
def test_base_none(self):
self.assertEqual(safe_join(None, 'path'), '')
def test_base_empty(self):
self.assertEqual(safe_join('', 'path'), '')
def test_tail_empty(self):
# joining with empty tail should yield base with trailing slash
self.assertEqual(
safe_join('http://example.com', ''),
'http://example.com/'
)
def test_numeric_base(self):
# numeric base is cast to string
self.assertEqual(safe_join(123, 'path'), '123/path')
def test_exception_in_str(self):
class Bad:
def __str__(self):
raise ValueError('bad')
# on exception, safe_join returns ''
self.assertEqual(safe_join(Bad(), 'x'), '')
if __name__ == '__main__':
unittest.main()