feat(form-builder): broadcast channel auth + listener layout comment update

Per RFC-WS-6 §Q1 v1.3 addition 2.

- routes/channels.php (NEW): authorization callback for the
  submission.{id} private channel. v1 authz scope is submitter-only
  (matches submitted_by_user_id); org-admin access is deferred per
  BACKLOG TECH-CHANNEL-AUTH-ORG-ADMIN. Frontend Echo subscription
  lands as a separate frontend follow-up.
- bootstrap/app.php: registers routes/channels.php via withRouting()
  channels: parameter. This is NEW broadcasting wiring — Laravel's
  broadcasting auth middleware was not previously connected to the
  framework. Without this registration the channels file is dead code.
- AppServiceProvider:👢 comment block updated to v1.3 listener
  layout (1 sync ApplyBindings + N queued, all gated on
  apply_status=COMPLETED per ARCH-BINDINGS §5.6). Comment on
  TriggerPersonIdentityMatch flipped from "(sync)" to "(queued
  post-v1.3)".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-08 02:57:22 +02:00
parent 2a8f108b0e
commit 912022f5da
3 changed files with 81 additions and 10 deletions

57
api/routes/channels.php Normal file
View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
use App\Models\FormBuilder\FormSubmission;
use App\Models\User;
use Illuminate\Support\Facades\Broadcast;
/*
|--------------------------------------------------------------------------
| Broadcast Channel Authorization
|--------------------------------------------------------------------------
|
| Channel-level authorization callbacks for private and presence channels.
| Laravel's broadcasting auth middleware invokes these on subscription
| attempts; returning truthy authorises, falsy denies.
|
| File registered in bootstrap/app.php via withRouting(channels: ...).
| Without that registration this file is dead code.
|
*/
/*
* Per RFC-WS-6 §Q1 v1.3 addition 2.
*
* Authorises private subscriptions to a form-submission's identity-match
* resolution channel. The TriggerPersonIdentityMatchOnFormSubmit listener
* dispatches FormSubmissionIdentityMatchResolved on this channel after
* writing the final identity_match_status; the frontend portal
* IdentityMatchBanner subscribes via Echo.private('submission.{id}') and
* refetches the submission resource on receipt.
*
* v1 authz scope: only the submitter who created the submission via an
* authenticated session is allowed to subscribe. Org-admin access is
* deferred see BACKLOG entry TECH-CHANNEL-AUTH-ORG-ADMIN. Public
* (token-based) submitters are not on this channel; their flow is
* already polling-based and they don't have a User to authenticate with.
*/
Broadcast::channel(
'submission.{submissionId}',
function (User $user, string $submissionId): bool {
$submission = FormSubmission::query()
->withoutGlobalScopes()
->find($submissionId);
if ($submission === null) {
return false;
}
// TODO TECH-CHANNEL-AUTH-ORG-ADMIN — extend to organisation admins
// once we audit the Spatie Permission helper for an
// organisation-scoped role check (hasRoleInOrganisation or
// similar). Until that audit lands, only the submitter has
// channel access.
return $submission->submitted_by_user_id === $user->id;
},
);