diff --git a/api/app/Providers/AppServiceProvider.php b/api/app/Providers/AppServiceProvider.php index f16031bd..a0718ada 100644 --- a/api/app/Providers/AppServiceProvider.php +++ b/api/app/Providers/AppServiceProvider.php @@ -163,16 +163,24 @@ class AppServiceProvider extends ServiceProvider FormField::observe(FormFieldChildTablesCascadeObserver::class); FormFieldLibrary::observe(FormFieldChildTablesCascadeObserver::class); - // RFC-WS-6 §3 (Q1) — sync chain on FormSubmissionSubmitted, in - // this exact order: - // 1. ApplyBindingsOnFormSubmit (sync) - // 2. TriggerPersonIdentityMatchOnFormSubmit (sync) - // Queued listeners on the same event (SyncTagPickerSelectionsOnSubmit, - // future webhook dispatcher, mailables) run in parallel after the - // sync chain via the queue. Their relative registration position - // is irrelevant. + // RFC-WS-6 v1.3 §Q1 — FormSubmissionSubmitted listener layout. + // + // SYNC chain (single listener): + // 1. ApplyBindingsOnFormSubmit + // + // QUEUED listeners (parallel, all gated on apply_status=COMPLETED + // per ARCH-BINDINGS §5.6 — PARTIAL and FAILED both fall through to + // the early-return): + // - TriggerPersonIdentityMatchOnFormSubmit (gates + invariant + broadcast) + // - SyncTagPickerSelectionsOnSubmit + // + // The listener-ordering structural test + // (FormSubmissionSubmittedListenerOrderTest) asserts ApplyBindings + // is the only sync listener and that every queued listener has + // the apply_status=COMPLETED gate as its first executable + // statement. - // RFC Q1 — applies bindings sync before identity match runs. + // RFC Q1 — applies bindings sync before queued siblings fire. \Illuminate\Support\Facades\Event::listen( FormSubmissionSubmitted::class, ApplyBindingsOnFormSubmit::class, @@ -184,7 +192,8 @@ class AppServiceProvider extends ServiceProvider SyncTagPickerSelectionsOnSubmit::class, ); - // ARCH §31.1 — identity-match trigger on event_registration (sync). + // ARCH §31.1 — identity-match trigger on event_registration (queued + // post-v1.3; was sync in v1.0/v1.2 layout). \Illuminate\Support\Facades\Event::listen( FormSubmissionSubmitted::class, TriggerPersonIdentityMatchOnFormSubmit::class, diff --git a/api/bootstrap/app.php b/api/bootstrap/app.php index 5d702be5..d8978393 100644 --- a/api/bootstrap/app.php +++ b/api/bootstrap/app.php @@ -19,6 +19,11 @@ return Application::configure(basePath: dirname(__DIR__)) web: __DIR__.'/../routes/web.php', api: __DIR__.'/../routes/api.php', commands: __DIR__.'/../routes/console.php', + // Per RFC-WS-6 §Q1 v1.3 addition 2 — broadcast channel auth callbacks + // live in routes/channels.php. Registers Laravel's broadcasting auth + // middleware so private/presence channel subscriptions reach the + // closures defined there. + channels: __DIR__.'/../routes/channels.php', health: '/up', apiPrefix: 'api/v1', ) diff --git a/api/routes/channels.php b/api/routes/channels.php new file mode 100644 index 00000000..b6ec7bc2 --- /dev/null +++ b/api/routes/channels.php @@ -0,0 +1,57 @@ +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; + }, +);