Skip to content

Commit 637bc7a

Browse files
authored
Enable outbound transport in --no-transport mode, only disable inbound (openwallet-foundation#4050)
* only disable inbound transport, not outbound Signed-off-by: Patrick St-Louis <patrick.st-louis@opsecid.ca> * fix "security" issues Signed-off-by: Patrick St-Louis <patrick.st-louis@opsecid.ca> --------- Signed-off-by: Patrick St-Louis <patrick.st-louis@opsecid.ca>
1 parent 2b126e9 commit 637bc7a

2 files changed

Lines changed: 143 additions & 8 deletions

File tree

acapy_agent/core/conductor.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -236,13 +236,18 @@ async def setup(self):
236236
)
237237
LOGGER.debug("Inbound transports registered successfully.")
238238

239-
# Register all outbound transports
240-
LOGGER.debug("Setting up outbound transports.")
241-
self.outbound_transport_manager = OutboundTransportManager(
242-
self.root_profile, self.handle_not_delivered
243-
)
239+
# Always register outbound transports (needed for webhook delivery
240+
# even when DIDComm transports are disabled via --no-transport)
241+
LOGGER.debug("Setting up outbound transports.")
242+
self.outbound_transport_manager = OutboundTransportManager(
243+
self.root_profile, self.handle_not_delivered
244+
)
245+
if context.settings.get("transport.disabled"):
246+
# In no-transport mode, register HTTP transport for webhook delivery
247+
self.outbound_transport_manager.register("http")
248+
else:
244249
await self.outbound_transport_manager.setup()
245-
LOGGER.debug("Outbound transports registered successfully.")
250+
LOGGER.debug("Outbound transports registered successfully.")
246251

247252
# Initialize dispatcher
248253
LOGGER.debug("Initializing dispatcher.")
@@ -335,14 +340,18 @@ async def start(self) -> None:
335340
LOGGER.debug("Wallet type validated.")
336341

337342
if not context.settings.get("transport.disabled"):
338-
# Start up transports if enabled
343+
# Start up inbound transports if enabled
339344
try:
340345
LOGGER.debug("Transport not disabled. Starting inbound transports.")
341346
await self.inbound_transport_manager.start()
342347
LOGGER.debug("Inbound transports started successfully.")
343348
except Exception:
344349
LOGGER.exception("Unable to start inbound transports.")
345350
raise
351+
352+
# Always start outbound transports (needed for webhook delivery
353+
# even when DIDComm transports are disabled via --no-transport)
354+
if self.outbound_transport_manager:
346355
try:
347356
LOGGER.debug("Starting outbound transports.")
348357
await self.outbound_transport_manager.start()
@@ -865,6 +874,11 @@ def webhook_router(
865874
metadata: Additional metadata associated with the payload
866875
867876
"""
877+
if not self.outbound_transport_manager:
878+
LOGGER.warning(
879+
"Cannot send webhook: outbound transport manager is not available"
880+
)
881+
return
868882
try:
869883
self.outbound_transport_manager.enqueue_webhook(
870884
topic, payload, endpoint, max_attempts, metadata

acapy_agent/core/tests/test_conductor.py

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1404,7 +1404,7 @@ async def test_webhook_router(self):
14041404

14051405
test_topic = "test-topic"
14061406
test_payload = {"test": "payload"}
1407-
test_endpoint = "http://example"
1407+
test_endpoint = "https://example"
14081408
test_attempts = 2
14091409

14101410
test_profile = await create_test_profile(None, await builder.build_context())
@@ -1449,6 +1449,127 @@ async def test_webhook_router(self):
14491449
test_topic, test_payload, test_endpoint, test_attempts, None
14501450
)
14511451

1452+
async def test_webhook_router_no_outbound_manager(self):
1453+
builder: ContextBuilder = StubContextBuilder(self.test_settings)
1454+
conductor = test_module.Conductor(builder)
1455+
conductor.outbound_transport_manager = None
1456+
1457+
with mock.patch.object(test_module, "LOGGER") as mock_logger:
1458+
conductor.webhook_router(
1459+
"test-topic", {"test": "payload"}, "https://example", 2
1460+
)
1461+
mock_logger.warning.assert_called_once()
1462+
1463+
async def test_setup_no_transport_mode(self):
1464+
builder: ContextBuilder = StubContextBuilder(self.test_settings)
1465+
builder.update_settings({"transport.disabled": True})
1466+
conductor = test_module.Conductor(builder)
1467+
1468+
test_profile = await create_test_profile(None, await builder.build_context())
1469+
1470+
with (
1471+
mock.patch.object(
1472+
test_module,
1473+
"wallet_config",
1474+
return_value=(
1475+
test_profile,
1476+
DIDInfo("did", "verkey", metadata={}, method=SOV, key_type=ED25519),
1477+
),
1478+
),
1479+
mock.patch.object(
1480+
test_module, "OutboundTransportManager", autospec=True
1481+
) as mock_outbound_mgr,
1482+
):
1483+
mock_outbound_mgr.return_value.registered_transports = {
1484+
"test": mock.MagicMock(schemes=["http"])
1485+
}
1486+
await conductor.setup()
1487+
1488+
# Inbound transports should NOT be set up
1489+
assert conductor.inbound_transport_manager is None
1490+
1491+
# Outbound transport manager should be created
1492+
mock_outbound_mgr.assert_called_once()
1493+
# HTTP transport should be registered for webhook delivery
1494+
mock_outbound_mgr.return_value.register.assert_called_once_with("http")
1495+
# setup() should NOT be called (that registers from config)
1496+
mock_outbound_mgr.return_value.setup.assert_not_awaited()
1497+
1498+
async def test_start_no_transport_mode(self):
1499+
builder: ContextBuilder = StubContextBuilder(self.test_settings)
1500+
builder.update_settings({"transport.disabled": True})
1501+
conductor = test_module.Conductor(builder)
1502+
1503+
test_profile = await create_test_profile(None, await builder.build_context())
1504+
1505+
with (
1506+
mock.patch.object(
1507+
test_module,
1508+
"wallet_config",
1509+
return_value=(
1510+
test_profile,
1511+
DIDInfo("did", "verkey", metadata={}, method=SOV, key_type=ED25519),
1512+
),
1513+
),
1514+
mock.patch.object(
1515+
test_module, "OutboundTransportManager", autospec=True
1516+
) as mock_outbound_mgr,
1517+
):
1518+
mock_outbound_mgr.return_value.registered_transports = {
1519+
"test": mock.MagicMock(schemes=["http"])
1520+
}
1521+
await conductor.setup()
1522+
1523+
mock_outbound_mgr.return_value.registered_transports = {}
1524+
1525+
await conductor.start()
1526+
1527+
# Outbound transports should still be started for webhooks
1528+
mock_outbound_mgr.return_value.start.assert_awaited_once_with()
1529+
1530+
await conductor.stop()
1531+
mock_outbound_mgr.return_value.stop.assert_awaited_once_with()
1532+
1533+
async def test_webhook_in_no_transport_mode(self):
1534+
builder: ContextBuilder = StubContextBuilder(self.test_settings)
1535+
builder.update_settings({"transport.disabled": True})
1536+
conductor = test_module.Conductor(builder)
1537+
1538+
test_topic = "test-topic"
1539+
test_payload = {"test": "payload"}
1540+
test_endpoint = "https://example"
1541+
test_attempts = 2
1542+
1543+
test_profile = await create_test_profile(None, await builder.build_context())
1544+
1545+
with (
1546+
mock.patch.object(
1547+
test_module,
1548+
"wallet_config",
1549+
return_value=(
1550+
test_profile,
1551+
DIDInfo("did", "verkey", metadata={}, method=SOV, key_type=ED25519),
1552+
),
1553+
),
1554+
mock.patch.object(
1555+
test_module, "OutboundTransportManager", autospec=True
1556+
) as mock_outbound_mgr,
1557+
):
1558+
mock_outbound_mgr.return_value.registered_transports = {
1559+
"test": mock.MagicMock(schemes=["http"])
1560+
}
1561+
await conductor.setup()
1562+
1563+
with mock.patch.object(
1564+
conductor.outbound_transport_manager, "enqueue_webhook"
1565+
) as mock_enqueue:
1566+
conductor.webhook_router(
1567+
test_topic, test_payload, test_endpoint, test_attempts
1568+
)
1569+
mock_enqueue.assert_called_once_with(
1570+
test_topic, test_payload, test_endpoint, test_attempts, None
1571+
)
1572+
14521573
async def test_shutdown_multitenant_profiles(self):
14531574
builder: ContextBuilder = StubContextBuilder(
14541575
{**self.test_settings, "multitenant.enabled": True}

0 commit comments

Comments
 (0)