Make delivery and integration failures explicit
Persist reminder presentations and retry state, atomically complete collapsed deliveries, fall back across away reaches, and block permanent failures visibly (V-715, V-678). Fail closed when enabled integrations lack credentials and keep remote arms explicitly dark (V-691). Give mavweb one sanitized, request-correlated error contract (V-689). Owner explicitly requested direct commits to master.
This commit is contained in:
+17
-2
@@ -20,6 +20,7 @@ every stutter on the way there.
|
||||
"""
|
||||
|
||||
import hmac
|
||||
import ipaddress
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
@@ -71,6 +72,18 @@ def authorised(headers):
|
||||
return hmac.compare_digest(got, "Bearer " + TOKEN)
|
||||
|
||||
|
||||
def validate_listener_auth(host, token):
|
||||
"""Refuse a network listener without its bearer boundary."""
|
||||
try:
|
||||
loopback = ipaddress.ip_address(host).is_loopback
|
||||
except ValueError:
|
||||
loopback = host.casefold() == "localhost"
|
||||
if not loopback and not token.strip():
|
||||
raise ValueError(
|
||||
f"CW2_TOKEN is required while CW2_HOST={host!r} is reachable from the network"
|
||||
)
|
||||
|
||||
|
||||
class Handler(BaseHTTPRequestHandler):
|
||||
protocol_version = "HTTP/1.1"
|
||||
|
||||
@@ -142,8 +155,10 @@ class Handler(BaseHTTPRequestHandler):
|
||||
|
||||
|
||||
def main():
|
||||
if not TOKEN:
|
||||
log.warning("no CW2_TOKEN set: anything on the LAN can post audio here")
|
||||
try:
|
||||
validate_listener_auth(HOST, TOKEN)
|
||||
except ValueError as exc:
|
||||
raise SystemExit(str(exc)) from exc
|
||||
# Bind before loading, so a restart answers 503 rather than refusing the
|
||||
# connection. Both make Maven fall back, but only one of them says why.
|
||||
srv = ThreadingHTTPServer((HOST, PORT), Handler)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import unittest
|
||||
|
||||
import serve
|
||||
|
||||
|
||||
class ListenerAuthTest(unittest.TestCase):
|
||||
def test_network_listener_requires_token(self):
|
||||
for host in ("0.0.0.0", "192.168.1.105", "::"):
|
||||
for token in ("", " "):
|
||||
with self.subTest(host=host, token=token):
|
||||
with self.assertRaises(ValueError):
|
||||
serve.validate_listener_auth(host, token)
|
||||
|
||||
def test_loopback_listener_may_be_explicitly_unauthenticated(self):
|
||||
for host in ("127.0.0.1", "::1", "localhost"):
|
||||
with self.subTest(host=host):
|
||||
serve.validate_listener_auth(host, "")
|
||||
|
||||
def test_network_listener_accepts_token(self):
|
||||
serve.validate_listener_auth("0.0.0.0", "secret")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user