Files
computer-playbook/tests/unit/python/utils/certs/test_tls_common.py
T
kevinveenbirkenbachandClaude Opus 5.5 9152c884d8 fix(tls): serve every redirect source the certificate that names it
The cert lookup handed an alias or redirect source the live directory of its app's canonical domain or of its target, so social.cymais.cloud got the infinito.nexus lineage and failed with 'Hostname mismatch'. With the san and dedicated flavors a domain term now resolves to its own live/<domain>, which cert_link_domains points at the lineage that covers it; application terms keep the canonical path. resolve_term follows redirect chains and fails on a cycle.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 20:14:21 +02:00

410 lines
14 KiB
Python

import importlib
import sys
import unittest
from ansible.errors import AnsibleError
from utils.tls_common import (
AVAILABLE_FLAVORS,
align_domain_to_consumer,
as_str,
collect_domains_for_app,
collect_domains_global,
get_path,
is_domain_term,
is_onion_domain,
norm_domain,
override_san_list,
redirect_targets,
require,
resolve_app_id_from_domain,
resolve_enabled,
resolve_le_name,
resolve_mode,
resolve_primary_domain_from_app,
resolve_term,
uniq_preserve,
want_get,
)
_tls_common = importlib.import_module("utils.tls_common")
sys.modules.setdefault("ansible.module_utils.tls_common", _tls_common)
class TestTlsCommon(unittest.TestCase):
def setUp(self):
self.domains = {
"web-app-a": "a.example",
"web-app-b": ["b.example", "b-alt.example"],
"web-app-c": {"primary": "c.example", "api": "api.c.example"},
}
def test_as_str_and_norm_domain(self):
self.assertEqual(as_str(" x "), "x")
self.assertEqual(norm_domain(" A.Example "), "a.example")
def test_require_success_and_fail(self):
v = {"X": 1}
self.assertEqual(require(v, "X", int), 1)
with self.assertRaises(AnsibleError):
require(v, "MISSING", int)
with self.assertRaises(AnsibleError):
require(v, "X", str)
def test_get_path(self):
data = {"a": {"b": {"c": 1}}}
self.assertEqual(get_path(data, "a.b.c"), 1)
self.assertIsNone(get_path(data, "a.b.x"))
self.assertEqual(get_path(data, "a.b.x", 42), 42)
def test_want_get(self):
data = {"a": {"b": 1}}
self.assertEqual(want_get(data, "a.b"), 1)
with self.assertRaises(AnsibleError):
want_get(data, "a.x")
def test_uniq_preserve_normalizes_and_dedupes(self):
items = ["A.EXAMPLE", "a.example", "b.example", "B.EXAMPLE", " "]
self.assertEqual(uniq_preserve(items), ["a.example", "b.example"])
def test_resolve_primary_domain_from_app(self):
self.assertEqual(
resolve_primary_domain_from_app(self.domains, "web-app-a", err_prefix="t"),
"a.example",
)
self.assertEqual(
resolve_primary_domain_from_app(self.domains, "web-app-b", err_prefix="t"),
"b.example",
)
self.assertEqual(
resolve_primary_domain_from_app(self.domains, "web-app-c", err_prefix="t"),
"c.example",
)
with self.assertRaises(AnsibleError):
resolve_primary_domain_from_app(self.domains, "missing", err_prefix="t")
def test_resolve_app_id_from_domain(self):
self.assertEqual(
resolve_app_id_from_domain(self.domains, "a.example", err_prefix="t"),
"web-app-a",
)
self.assertEqual(
resolve_app_id_from_domain(self.domains, "API.C.EXAMPLE", err_prefix="t"),
"web-app-c",
)
with self.assertRaises(AnsibleError):
resolve_app_id_from_domain(self.domains, "nope.example", err_prefix="t")
def test_collect_domains_for_app(self):
self.assertEqual(
collect_domains_for_app(self.domains, "web-app-a", err_prefix="t"),
["a.example"],
)
self.assertEqual(
collect_domains_for_app(self.domains, "web-app-b", err_prefix="t"),
["b.example", "b-alt.example"],
)
self.assertEqual(
collect_domains_for_app(self.domains, "web-app-c", err_prefix="t"),
["c.example", "api.c.example"],
)
def test_collect_domains_global(self):
got = collect_domains_global(self.domains)
self.assertEqual(
got,
["a.example", "b.example", "b-alt.example", "c.example", "api.c.example"],
)
def test_resolve_term_domain_and_app(self):
app_id, primary = resolve_term(
"API.C.EXAMPLE", domains=self.domains, forced_mode="auto", err_prefix="t"
)
self.assertEqual(app_id, "web-app-c")
self.assertEqual(primary, "api.c.example")
app_id, primary = resolve_term(
"web-app-b", domains=self.domains, forced_mode="app", err_prefix="t"
)
self.assertEqual(app_id, "web-app-b")
self.assertEqual(primary, "b.example")
with self.assertRaises(AnsibleError):
resolve_term(
"x", domains=self.domains, forced_mode="invalid", err_prefix="t"
)
def test_resolve_term_redirect_source_resolves_to_target(self):
app_id, primary = resolve_term(
"B-Auto.Example",
domains=self.domains,
applications={},
redirects=redirect_targets(
{
"CURRENT_PLAY_REDIRECT_DOMAINS": [
{"source": "b-auto.example", "target": "B.example"}
]
}
),
forced_mode="auto",
err_prefix="t",
)
self.assertEqual(app_id, "web-app-b")
self.assertEqual(primary, "b.example")
def test_resolve_term_unknown_domain_still_fails_without_redirect(self):
for variables in ({}, {"CURRENT_PLAY_REDIRECT_DOMAINS": None}):
with self.assertRaises(AnsibleError):
resolve_term(
"b-auto.example",
domains=self.domains,
applications={},
redirects=redirect_targets(variables),
forced_mode="auto",
err_prefix="t",
)
def test_resolve_term_redirect_follows_a_chain(self):
app_id, primary = resolve_term(
"*.legacy.example",
domains=self.domains,
applications={},
redirects={"*.legacy.example": "two.example", "two.example": "b.example"},
forced_mode="auto",
err_prefix="t",
)
self.assertEqual((app_id, primary), ("web-app-b", "b.example"))
def test_resolve_term_redirect_cycle_fails(self):
with self.assertRaises(AnsibleError):
resolve_term(
"one.example",
domains=self.domains,
applications={},
redirects={"one.example": "two.example", "two.example": "one.example"},
forced_mode="auto",
err_prefix="t",
)
def test_is_domain_term(self):
self.assertTrue(is_domain_term("a.example", "auto", err_prefix="t"))
self.assertFalse(is_domain_term("web-app-a", "auto", err_prefix="t"))
self.assertTrue(is_domain_term("web-app-a", "domain", err_prefix="t"))
self.assertFalse(is_domain_term("a.example", "app", err_prefix="t"))
with self.assertRaises(AnsibleError):
is_domain_term("a.example", "other", err_prefix="t")
def test_resolve_enabled_and_mode(self):
app = {}
self.assertTrue(resolve_enabled(app, True))
self.assertFalse(resolve_enabled({"server": {"tls": {"enabled": False}}}, True))
self.assertEqual(
resolve_mode(app, True, "letsencrypt", err_prefix="t"), "letsencrypt"
)
self.assertEqual(resolve_mode(app, False, "letsencrypt", err_prefix="t"), "off")
app2 = {"server": {"tls": {"flavor": "self_signed"}}}
self.assertEqual(
resolve_mode(app2, True, "letsencrypt", err_prefix="t"), "self_signed"
)
with self.assertRaises(AnsibleError):
resolve_mode(
{"server": {"tls": {"flavor": "nope"}}},
True,
"letsencrypt",
err_prefix="t",
)
def test_is_onion_domain(self):
self.assertTrue(is_onion_domain("abc.onion"))
self.assertTrue(is_onion_domain("next.cloud.ABC123.ONION"))
self.assertFalse(is_onion_domain("example.com"))
self.assertFalse(is_onion_domain("onion.example.com"))
self.assertFalse(is_onion_domain(""))
def test_resolve_enabled_onion_always_off(self):
self.assertFalse(resolve_enabled({}, True, primary_domain="x.onion"))
self.assertFalse(
resolve_enabled(
{"server": {"tls": {"enabled": True}}},
True,
primary_domain="app.abc.onion",
)
)
self.assertTrue(resolve_enabled({}, True, primary_domain="example.com"))
def test_align_domain_to_consumer_clearnet_consumer_gets_clearnet(self):
domains = {
"web-svc-cdn": ["cdn.abc.onion", "cdn.example"],
"web-app-bigbluebutton": ["bbb.example"],
}
self.assertEqual(
align_domain_to_consumer(
domains,
"web-svc-cdn",
"cdn.abc.onion",
consumer="web-app-bigbluebutton",
),
"cdn.example",
)
def test_align_domain_to_consumer_reads_variables_application_id(self):
domains = {
"web-svc-cdn": ["cdn.abc.onion", "cdn.example"],
"web-app-bigbluebutton": ["bbb.example"],
}
self.assertEqual(
align_domain_to_consumer(
domains,
"web-svc-cdn",
"cdn.abc.onion",
variables={"application_id": "web-app-bigbluebutton"},
),
"cdn.example",
)
def test_align_domain_to_consumer_onion_consumer_unchanged(self):
domains = {
"web-svc-cdn": ["cdn.abc.onion", "cdn.example"],
"web-app-dashboard": ["dash.abc.onion"],
}
self.assertEqual(
align_domain_to_consumer(
domains, "web-svc-cdn", "cdn.abc.onion", consumer="web-app-dashboard"
),
"cdn.abc.onion",
)
def test_align_domain_to_consumer_onion_only_target_falls_back(self):
domains = {
"web-svc-cdn": ["cdn.abc.onion"],
"web-app-bigbluebutton": ["bbb.example"],
}
self.assertEqual(
align_domain_to_consumer(
domains,
"web-svc-cdn",
"cdn.abc.onion",
consumer="web-app-bigbluebutton",
),
"cdn.abc.onion",
)
def test_align_domain_to_consumer_clearnet_target_untouched(self):
domains = {
"web-svc-cdn": ["cdn.example"],
"web-app-bigbluebutton": ["bbb.example"],
}
self.assertEqual(
align_domain_to_consumer(
domains, "web-svc-cdn", "cdn.example", consumer="web-app-bigbluebutton"
),
"cdn.example",
)
def test_align_domain_to_consumer_no_or_self_or_unknown_consumer(self):
domains = {
"web-svc-cdn": ["cdn.abc.onion", "cdn.example"],
"web-app-bigbluebutton": ["bbb.example"],
}
self.assertEqual(
align_domain_to_consumer(domains, "web-svc-cdn", "cdn.abc.onion"),
"cdn.abc.onion",
)
self.assertEqual(
align_domain_to_consumer(
domains, "web-svc-cdn", "cdn.abc.onion", consumer="web-svc-cdn"
),
"cdn.abc.onion",
)
self.assertEqual(
align_domain_to_consumer(
domains, "web-svc-cdn", "cdn.abc.onion", consumer="svc-db-postgres"
),
"cdn.abc.onion",
)
def test_align_domain_to_consumer_templates_consumer(self):
class _FakeTemplar:
def template(self, value):
return {"{{ application_id }}": "web-app-bigbluebutton"}.get(
value, value
)
domains = {
"web-svc-cdn": ["cdn.abc.onion", "cdn.example"],
"web-app-bigbluebutton": ["bbb.example"],
}
self.assertEqual(
align_domain_to_consumer(
domains,
"web-svc-cdn",
"cdn.abc.onion",
variables={"application_id": "{{ application_id }}"},
templar=_FakeTemplar(),
),
"cdn.example",
)
def test_align_domain_to_consumer_dict_target_picks_clearnet_value(self):
domains = {
"web-app-matrix": {
"synapse_onion": "matrix.abc.onion",
"element_onion": "element.abc.onion",
"synapse": "matrix.example",
"element": "element.example",
},
"web-app-bigbluebutton": ["bbb.example"],
}
self.assertEqual(
align_domain_to_consumer(
domains,
"web-app-matrix",
"matrix.abc.onion",
consumer="web-app-bigbluebutton",
),
"matrix.example",
)
def test_resolve_mode_app_level_mode_over_flavor(self):
app = {"server": {"tls": {"mode": "self_signed", "flavor": "letsencrypt"}}}
self.assertEqual(
resolve_mode(app, True, "letsencrypt", err_prefix="t"), "self_signed"
)
app2 = {"server": {"tls": {"flavor": "self_signed"}}}
self.assertEqual(
resolve_mode(app2, True, "letsencrypt", err_prefix="t"), "self_signed"
)
def test_resolve_le_name(self):
app = {}
self.assertEqual(resolve_le_name(app, "x.example"), "x.example")
app2 = {"server": {"tls": {"letsencrypt_cert_name": "mycert"}}}
self.assertEqual(resolve_le_name(app2, "x.example"), "mycert")
def test_override_san_list(self):
self.assertIsNone(override_san_list({}))
self.assertEqual(
override_san_list({"server": {"tls": {"domains_san": "alt.example"}}}),
["alt.example"],
)
self.assertEqual(
override_san_list({"server": {"tls": {"domains_san": ["a", "b", ""]}}}),
["a", "b"],
)
self.assertEqual(
override_san_list({"server": {"tls": {"domains_san": {"x": "y"}}}}),
[],
)
def test_available_flavors(self):
self.assertIn("letsencrypt", AVAILABLE_FLAVORS)
self.assertIn("self_signed", AVAILABLE_FLAVORS)
if __name__ == "__main__":
unittest.main()