feat(drivers): expose authenticated RPC caller to @rpc handlers#54
Conversation
Add get_rpc_source_device() so device drivers can perform per-call authorization without changing handler signatures. The @rpc wrapper already receives the authenticated source device id; it now also publishes it on a contextvar for the duration of the handler call and resets it afterwards. This enables drivers to enforce caller-based access control on state-mutating RPCs (security hardening) while remaining fully backward compatible: handlers that do not call get_rpc_source_device() are unaffected, and the value is None when no source identity is present (e.g. local routine/internal calls). - drivers/decorators.py: _rpc_source_device contextvar + accessor, set/reset around the handler invocation - drivers/__init__.py: export get_rpc_source_device - tests: cover visible-inside-handler, None-when-absent, reset-after, and kwarg non-leak
kavya-chennoju
left a comment
There was a problem hiding this comment.
Approving — tested end to end.
E2E (real D2D, Zenoh mesh): ran two live DeviceRuntime devices (controller-1 + sensor-001); controller-1 called invoke_remote("sensor-001", "whoami"). The runtime stamped source_device itself, and the handler read it back via get_rpc_source_device():
[sensor-001] whoami handler sees source_device = 'controller-1'
PASS: handler saw caller='controller-1' (expected 'controller-1')
Also confirmed the absent path: an agent-tools caller (not a device) yields caller=None, as intended. Unit tests pass (54).
The contextvar plumbing, reset-after-call, and no-kwarg-leak all behave correctly. LGTM as a caller-identity hook.
Non-blocking note for a follow-up: source_device is currently self-asserted by the caller and not yet bound to a transport-authenticated identity, so it shouldn't be relied on for authorization decisions until receiver-side identity verification lands. Tracking that separately.
Summary
Adds
get_rpc_source_device()so device drivers can perform per-call authorization without changing their handler signatures.The
@rpcwrapper already receives the authenticated source device id (injected byDeviceRuntime). This change additionally publishes it on a contextvar for the duration of the handler call and resets it afterwards, so a handler can look up who called it and gate state-mutating operations accordingly.Motivation
Device drivers that wrap physical hardware need to authorize callers on state-mutating RPCs (e.g. execute/stop). Today the source identity is only used for logging/tracing and is popped before the handler body runs. Exposing it via a small, opt-in accessor lets driver authors add caller-based access control as a security-hardening measure.
Backward compatibility
Fully backward compatible:
get_rpc_source_device()are unaffected.Nonewhen no source identity is present (local routine/internal calls).source_deviceis still consumed by the wrapper (not leaked into handler kwargs).Changes
drivers/decorators.py:_rpc_source_devicecontextvar +get_rpc_source_device()accessor; set/reset around the handler invocation.drivers/__init__.py: exportget_rpc_source_device.tests/test_rpc_source_device.py: visible-inside-handler, None-when-absent, reset-after-call, and kwarg-non-leak.Tests