fix(webhooks): authenticate deliveries via WEBHOOK_SECRET; review nits
Adds optional shared-secret authentication for /webhooks/nextcloud, addressing the security follow-up flagged in #747. Behavior: - WEBHOOK_SECRET set: registrations pass authMethod="header" with authData={"Authorization": "Bearer <secret>"} (encrypted at-rest in Nextcloud's DB and forwarded on every delivery). The receiver validates the same header with hmac.compare_digest before parsing any payload; missing/invalid → 401. - WEBHOOK_SECRET unset: registrations stay on authMethod="none" and the receiver accepts unauthenticated POSTs (logging a one-time startup warning). Backward compatible — operators can roll out at their own pace. Implementation notes: - WebhooksClient.create_webhook gains an `auth_data` parameter mapped to NC's `authData` body field; this is distinct from the existing `headers` parameter (`headers` is plaintext static request headers, `authData` is encrypted at-rest in NC and only emitted when authMethod="header"). The previous `auth_method="bearer"` mention in the docstring was incorrect — NC supports only "none" and "header". - A small `webhook_auth_pair()` helper in auth/webhook_routes.py centralises the secret→(auth_method, auth_data) resolution so the preset flow and the Astrolabe-facing /api/v1/webhooks endpoint stay in sync. Also addresses the smaller review points from #747: - f-string → lazy %s formatting in webhook_receiver.py and webhook_routes.py. - Move `int(time)` inside webhook_parser's try/except so a malformed `time` field returns None instead of raising ValueError. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
2e2a098bee
commit
224428fca5
@@ -41,6 +41,7 @@ class WebhooksClient(BaseNextcloudClient):
|
||||
http_method: str = "POST",
|
||||
auth_method: str = "none",
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
auth_data: Optional[Dict[str, str]] = None,
|
||||
event_filter: Optional[Dict[str, Any]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""Register a new webhook for the specified event.
|
||||
@@ -49,8 +50,14 @@ class WebhooksClient(BaseNextcloudClient):
|
||||
event: Fully qualified event class name (e.g., "OCP\\Files\\Events\\Node\\NodeCreatedEvent")
|
||||
uri: Webhook endpoint URL to receive event notifications
|
||||
http_method: HTTP method for webhook delivery (default: "POST")
|
||||
auth_method: Authentication method ("none", "bearer", etc.)
|
||||
headers: Custom headers to include in webhook requests (e.g., Authorization header)
|
||||
auth_method: Authentication method. Nextcloud's webhook_listeners
|
||||
app accepts only ``"none"`` or ``"header"``.
|
||||
headers: Optional static request headers attached to every
|
||||
delivery (stored in clear text on the NC side).
|
||||
auth_data: When ``auth_method="header"``, a dict of headers
|
||||
holding the auth credentials. Stored encrypted at-rest in
|
||||
Nextcloud's database and merged into the delivery request
|
||||
at send time. Required when ``auth_method="header"``.
|
||||
event_filter: JSON object specifying event filters (e.g., {"user.uid": "bob"})
|
||||
|
||||
Returns:
|
||||
@@ -66,6 +73,9 @@ class WebhooksClient(BaseNextcloudClient):
|
||||
if headers:
|
||||
data["headers"] = headers
|
||||
|
||||
if auth_data:
|
||||
data["authData"] = auth_data
|
||||
|
||||
if event_filter:
|
||||
data["eventFilter"] = event_filter
|
||||
|
||||
|
||||
Reference in New Issue
Block a user