feat(UI): fix migration, errors happening in local deploy and swarm integraton issues, tor merged and implement stalwart with tor

This commit is contained in:
Alejandro Roman committed 2026-09-01 12:34:57 +04:00
1 parent 333079cc2a
commit accc9e4c7c
18 files changed
+421 -37

No files matched your search

@@ -309,6 +309,68 @@ class TestEmailLookup(unittest.TestCase):
self.assertTrue(result["auth"])
self.assertFalse(result["start_tls"])
def test_a_provider_without_plaintext_submission_falls_back_to_its_mx(
self,
) -> None:
"""Stalwart's default config binds the implicit-TLS client ports and the
MX, never 587, so the onion path (which drops TLS) has no submission
listener to reach. The port follows what the provider declares, and AUTH
drops with it: an MX offers none, and announcing it aborts msmtp with
EX_UNAVAILABLE before a message is ever sent."""
_write_role_config(
self._tmp,
"web-app-mailprov",
{"mailprov": {"ports": {"public": {"smtp": 25, "smtps": 465}}}},
)
variables = {
"MAIL_PROVIDER": "web-app-mailprov",
"group_names": ["web-app-mailprov"],
"groups": {"web-app-mailprov": ["host1"]},
"SYSTEM_EMAIL_HOST": "mail.x.onion",
"TLS_ENABLED": True,
"DOMAIN_PRIMARY": "x.onion",
"inventory_hostname": "host1",
}
result = self.lookup.run(
[], variables=variables, roles_dir=str(self._tmp / "roles")
)[0]
self.assertFalse(result["tls"])
self.assertEqual(result["port"], 25)
self.assertFalse(result["auth"])
self.assertEqual(result["auth_mechanism"], "off")
def test_a_provider_declaring_submission_keeps_the_authenticated_path(
self,
) -> None:
"""Mailu binds 587, so the same onion target stays on plaintext
submission with AUTH PLAIN. The MX fallback above must not reach a
provider that declares a submission listener."""
_write_role_config(
self._tmp,
"web-app-mailprov",
{
"mailprov": {
"ports": {"public": {"smtp": 25, "smtps": 465, "submission": 587}}
}
},
)
variables = {
"MAIL_PROVIDER": "web-app-mailprov",
"group_names": ["web-app-mailprov"],
"groups": {"web-app-mailprov": ["host1"]},
"SYSTEM_EMAIL_HOST": "mail.x.onion",
"TLS_ENABLED": True,
"DOMAIN_PRIMARY": "x.onion",
"inventory_hostname": "host1",
}
result = self.lookup.run(
[], variables=variables, roles_dir=str(self._tmp / "roles")
)[0]
self.assertFalse(result["tls"])
self.assertEqual(result["port"], 587)
self.assertTrue(result["auth"])
self.assertEqual(result["auth_mechanism"], "plain")
def test_sso_relay_active_with_untemplated_enabled_gate(self) -> None:
# The role default gates sso.enabled on group_names (raw Jinja here);
# the guard must not treat the untemplated string as false.
@@ -37,6 +37,21 @@ class _DummyTemplar:
return value
class _RenderingTemplar:
"""A templar that actually resolves expressions, like the real one.
``_DummyTemplar`` returns its input unchanged, so it cannot expose a value
that only misbehaves while still an unrendered template.
"""
def __init__(self, available_variables=None, rendered=None):
self.available_variables = available_variables or {}
self._rendered = rendered or {}
def template(self, value):
return self._rendered.get(value, value)
def _email(external=True, host=RELAY):
return {"external": external, "host": host}
@@ -67,6 +82,18 @@ class TestSmtpHostLookup(unittest.TestCase):
vars_ = {"DOCKER_IN_CONTAINER": True, "DEPLOYMENT_MODE": "compose"}
self.assertEqual(self._resolve(vars_), "127.0.0.1")
def test_an_unrendered_group_var_still_reaches_the_rig_loopback(self):
"""DOCKER_IN_CONTAINER is a group_vars expression, and a lookup receives it
raw. Read unrendered it is neither true nor false, so the rig kept the
configured relay instead of loopback — and on an onion node that relay is a
.onion, which msmtp cannot route (EX_UNAVAILABLE, 'proxy failure')."""
raw = "{{ (ansible_facts['env']['container'] | default('')) != '' }}"
vars_ = {"DOCKER_IN_CONTAINER": raw, "DEPLOYMENT_MODE": "compose"}
lm = self.LookupModule()
lm._templar = _RenderingTemplar(vars_, {raw: True})
lm._loader = None
self.assertEqual(lm.run([_email()], variables=vars_)[0], "127.0.0.1")
def test_swarm_node_uses_loopback(self):
vars_ = {
"DOCKER_IN_CONTAINER": True,