[4.x] BroadcastingConfigBootstrapper: fix central instances persisting in tenant context#1448
[4.x] BroadcastingConfigBootstrapper: fix central instances persisting in tenant context#1448lukinovec wants to merge 57 commits into
Conversation
Test that BroadcastingConfigBootstrapper correctly maps tenant properties to broadcasting config/credentials, and that the credentials don't leak when switching contexts. Also add the `$config` property to `TestingBroadcaster` so that we can access the credentials used by the broadcaster.
…ators Test that TenancyBroadcastManager inherits the custom driver creators from the central BroadcastManager.
…anager's custom creators
…tenant's broadcaster on `bootstrap()`
…ed `Broadcasting\Factory` instance After initializing tenancy, calls like `Broadcast::auth()` use the central `BroadcastManager`. Clearing the facade's resolved `Broadcasting\Factory` instance fixes that problem.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1448 +/- ##
============================================
+ Coverage 86.57% 86.60% +0.02%
+ Complexity 1221 1219 -2
============================================
Files 187 186 -1
Lines 3575 3575
============================================
+ Hits 3095 3096 +1
+ Misses 480 479 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Moved binding `Broadcaster` to the bootstrapper.
Test that the bound Broadcaster instance inherits the channels too. Also test that the channels aren't lost when switching context to another tenant.
Tests from BroadcastingTest moved to the appropriate bootstrapper test files. The new tenant credentials test has assertions equal to both the original property -> config mapping test and the config -> credentials test.
Tests now use datasets with all drivers that are in `TenancyBroadcastManager::$tenantBroadcasters` by default plus the custom driver. Also add assertions for updating the tenant properties/config in tenant context.
… order
Previously, credential mappings from `$mapPresets` overrode mappings defined in `$credentialsMap`. If someone used pusher/reverb/ably and wanted to override some of that preset's mappings, e.g. use 'pusher_app_key' instead of 'pusher_key' by specifying 'pusher_app_key' in `$credentialsMap`, the preset's mapping ('pusher_key') would still be used.
Adding 'reverb' to `TenancyBroadcastManager::$tenantBroadcasters` will make these tests pass.
📝 WalkthroughWalkthroughBroadcasting now switches manager and broadcaster resolution during tenancy, refreshes facade bindings, applies tenant credential mappings, inherits channels, and restores central state. Tests cover multiple drivers, custom creators, channel persistence, isolation, helper registration, and lifecycle cleanup. ChangesTenant-aware broadcasting
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Tenancy
participant BroadcastingConfigBootstrapper
participant BroadcastManager
participant Broadcaster
participant BroadcastFacade
Tenancy->>BroadcastingConfigBootstrapper: initialize tenant context
BroadcastingConfigBootstrapper->>BroadcastManager: bind fresh tenant manager
BroadcastingConfigBootstrapper->>Broadcaster: bind tenant default connection
BroadcastingConfigBootstrapper->>BroadcastFacade: clear resolved instance
BroadcastFacade->>BroadcastManager: resolve tenant broadcaster
Tenancy->>BroadcastingConfigBootstrapper: end tenant context
BroadcastingConfigBootstrapper->>BroadcastFacade: restore central resolution
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/Bootstrappers/BroadcastingConfigBootstrapper.php`:
- Around line 96-98: The closure passed to $this->app->extend currently declares
an unused Broadcaster $broadcaster parameter which triggers static analysis;
change the callback signature to remove that parameter so it becomes a
zero-argument closure (e.g., function () { ... } or an arrow fn) and return
$this->app->make(BroadcastManager::class)->connection(); leaving the extend call
and references to Broadcaster::class and BroadcastManager::class unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 36a92dc5-b8bc-4656-be92-2b81ddd77b4c
📒 Files selected for processing (6)
src/Bootstrappers/BroadcastingConfigBootstrapper.phpsrc/Overrides/TenancyBroadcastManager.phptests/Bootstrappers/BroadcastChannelPrefixBootstrapperTest.phptests/Bootstrappers/BroadcastingConfigBootstrapperTest.phptests/BroadcastingTest.phptests/Etc/TestingBroadcaster.php
💤 Files with no reviewable changes (1)
- tests/BroadcastingTest.php
| // Change the BroadcastManager and Broadcaster singletons back to what they were before initializing tenancy | ||
| $this->app->singleton(BroadcastManager::class, fn (Application $app) => $this->originalBroadcastManager); | ||
| // Revert the bound BroadcastManager and Broadcaster singletons back to their original state | ||
| $this->app->singleton(BroadcastManager::class, fn (Application $app): ?BroadcastManager => $this->originalBroadcastManager); |
There was a problem hiding this comment.
What's the reason for the added typehint here? We don't use it with the other singleton reset and it wasn't here before
There was a problem hiding this comment.
Definitely a leftover. My IDE shows typehints like these as gray text, and when I click that text accidentally, it just adds the typehint -- so my bad for including the change in a commit here..
The typehint adds nothing, so I'd just remove it (also for consistency with the other singleton reset)
Now that we're dealing with this, maybe fn (Application $app) => ... could be changed to fn () => ..., since that Application typehint adds nothing (and $app isn't used at all)? Seems like in code similar to this, we only add have $app in the closure's params when it's used, so removing this would make things more consistent.
There was a problem hiding this comment.
Took care of both the typehint and the unused $app here: 24aeb23
| * Names of broadcasters that | ||
| * - should always be recreated using $this->resolve(), even when they're cached and available | ||
| * in $this->drivers so that when you update broadcasting config in the tenant context, | ||
| * the updated config/credentials will be used for broadcasting immediately. | ||
| * Note that in cases like this, only direct config changes are reflected right away. | ||
| * For the broadcasters to reflect tenant property changes made in tenant context, | ||
| * you still have to reinitialize tenancy after updating the tenant properties intended | ||
| * to be mapped to broadcasting config, since the properties are only mapped to config | ||
| * on BroadcastingConfigBootstrapper::bootstrap(). | ||
| * - should inherit the original broadcaster's channels (= the channels registered in | ||
| * the central context, e.g. in routes/channels.php, before this manager overrides the bound BroadcastManager). |
There was a problem hiding this comment.
I'm thinking if there is a reason for breaking the cache like this. When would someone update the config? Only when tenancy is being initialized - the config is loaded from the tenant and then stays the same. There aren't many use cases for someone updating broadcasting config long after tenancy is initialized.
If they would make any change, it'd be through changes on the tenant itself which aren't reflected in the config anyway, so a reinitialization would be needed.
The only benefit of doing this that I can see is that it makes our tests a little easier maybe? Fewer reinit calls needed.
There was a problem hiding this comment.
Right. I agree that there probably aren't many use cases for updating the config after tenancy is initialized, so I guess we can forget about it entirely.
Even with this re-resolving logic, after direct broadcasting config updates, a page reload is necessary for the frontend to reconnect to Echo using the new credentials. The re-resolving logic here loses its purpose, the channel-passing logic is the thing we care about here.
Now I found out that when the drivers are re-resolved, only the central channels get passed to them. So when someone registers a channel in tenant context (pretty unlikely but possible), and the driver gets re-resolved later e.g. during the /broadcasting/auth request (during Broadcast::auth()), the driver will only have the channels registered in the central context again (since the driver got re-resolved, its channel list is fresh, until we pass the central channels to it -- and then, it'll only have the central channels in the list). So the /broadcasting/auth request would throw a 403 as if the channel never got registered because the driver just doesn't have it in its list at this point due to the re-resolution. This is probably not a huge problem, but it's not right.
If we don't care about direct config updates and just establish that "for broadcasters to use the up-to-date credentials/config, tenancy has to be reinitialized", TenancyBroadcastManager could actually get simplified a lot. Passing the channels to freshly resolved broadcasters could be its only purpose (after initializing tenancy, all drivers would get freshly resolved because we create a new TenancyBroadcastManager instance with no drivers cached). Instead of overriding get() (which we do now just so that the broadcasters are always re-resolved), we could override resolve() just so that the freshly resolved broadcasters receive the central channels (and if someone registers a channel in the tenant context, that'll work without a problem because we won't re-resolve the broadcasters).
We could get rid of TenancyBroadcastManager::$tenantBroadcasters entirely since its purpose was to list names of broadcasters that should get re-resolved and receive the central channels (the coupling here was weird anyway). Since in this theoretical implementation, we'd just be passing central channels to broadcasters newly resolved (and then cached) by TenancyBroadcastManager, I think it'd be pretty safe to just do this for all broadcasters, not just some specific ones.
Anyways, these are pretty big changes. I'll try changing the code around and see if the changes would make sense in the end.
(Also, yeah, the tests would have to get updated)
There was a problem hiding this comment.
I'll try changing the code around and see if the changes would make sense in the end.
Tried that, and it turned out good. Will push the changes after polishing everything.
Worth noting that now that there's no re-resolving going on, app(BroadcasterContract::class) and Broadcast::driver() will be the same instances in tenant context (which is consistent with Laravel's behavior).
Now I found out that when the drivers are re-resolved, only the central channels get passed to them. So when someone registers a channel in tenant context (pretty unlikely but possible), and the driver gets re-resolved later e.g. during the /broadcasting/auth request (during Broadcast::auth()), the driver will only have the channels registered in the central context again
Added a regression test for this and made it pass -- a channel registered via Broadcast::channel() in tenant context now survives subsequent driver() calls (the test fails with the original get() override, passes with the new resolve() one). Also tested that the channels registered in tenant context only live for the duration of that context, so the channels don't leak from tenant to central context, and they don't leak from one tenant's context to another's.
There was a problem hiding this comment.
In the end, I deleted the entire TenancyBroadcastManager class in 9e6e0a9 as its only purpose was to pass the central channel auth closures to each newly resolved broadcaster in the tenant context. Only the default broadcaster's channel auth closures matter.
(see the PR's description)
…e invalid "notification sending" example
…t::clearResolvedInstance calls Also in bootstrap(), update the clearResolvedInstance comment. Remove the FQCN mention and explain why exactly do we need to clear the resolved instance
…on(...)` calls in `BroadcastingConfigBootstrapper::revert`)
…on instead of re-resolving them every time TenancyBroadcastManager now overrides resolve() (only to pass the central channels to the newly resolved broadcasters) instead of get(), so broadcasters stay cached for the duration of the tenant's context. Remove TenancyBroadcastManager::$tenantBroadcasters -- all broadcasters now inherit the central channels, so even custom drivers work without any additional configuration/registration. Channels registered via Broadcast::channel() in tenant context are no longer lost on subsequent broadcaster retrievals (e.g. during /broadcasting/auth). Added a dedicated test for this,
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/Overrides/TenancyBroadcastManager.php`:
- Around line 27-42: Update TenancyBroadcastManager::resolve() to avoid
resolving BroadcasterContract through the container after the manager swap,
which can recurse via BroadcastManager::connection(); instead, preserve the
already-resolved central broadcaster by injecting or otherwise passing it
directly into the manager before resolve() runs, then use that instance in
passChannelsFromOriginalBroadcaster() while retaining the existing Broadcaster
type checks.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: c6f806ae-4d43-4d09-b639-9d9563a4faca
📒 Files selected for processing (3)
src/Bootstrappers/BroadcastingConfigBootstrapper.phpsrc/Overrides/TenancyBroadcastManager.phptests/Bootstrappers/BroadcastingConfigBootstrapperTest.php
Use "userName" in the channel helper test's comment for consistency (the channel name uses "userName", the comment used "userId", fixed that)
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
|
@coderabbitai full review |
✅ Action performedFull review finished. |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/Bootstrappers/BroadcastingConfigBootstrapper.php`:
- Around line 83-106: Wrap the BroadcastManager and Broadcaster extension
mutations plus Broadcast facade reset in a try/catch within the bootstrapper
method, and call this bootstrapper’s revert logic before rethrowing any
exception from connection() resolution. Ensure rollback also handles partial
setup before the bootstrapper is tracked. Add a regression test using a custom
driver creator that throws during connection(), asserting the configuration and
manager bindings are restored.
In `@tests/Bootstrappers/BroadcastChannelPrefixBootstrapperTest.php`:
- Around line 196-221: Update the re-registration assertions in the
tenant_channel test to retrieve the callback actually stored under the prefixed
channel name from getChannels() after the second tenant_channel() call. Use that
retrieved closure, rather than the locally assigned $tenantChannelClosure, for
all subsequent central- and tenant-context invocations while retaining the
identity assertion against $centralChannelClosure.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: e8d31116-090a-437c-bb0d-e8304b55b593
📒 Files selected for processing (6)
src/Bootstrappers/BroadcastingConfigBootstrapper.phpsrc/Overrides/TenancyBroadcastManager.phptests/Bootstrappers/BroadcastChannelPrefixBootstrapperTest.phptests/Bootstrappers/BroadcastingConfigBootstrapperTest.phptests/BroadcastingTest.phptests/Etc/TestingBroadcaster.php
💤 Files with no reviewable changes (1)
- tests/BroadcastingTest.php
…nel() actually stored #1448 (comment)
…ly in the Broadcaster extend() closure Channel auth closures (registered using Broadcast::channel() e.g. in routes/channels.php) are only ever read from the default broadcaster -- both Broadcast::channel() and Broadcast::auth() go through the default broadcaster. TenancyBroadcastManager passed the closures to every broadcaster it resolved, which is pointless -- even in central context, Laravel doesn't do anything like that). Instead, bind a fresh BroadcastManager and pass the central channel closures to its default broadcaster while extending Broadcaster. So now, tenant context behaves exactly like central context, just with broadcasters resolved using the tenant credentials. Also update comments.
Remove the with() -- the test ran four times with the same TestingBroadcaster, so the only tested broadcaster was actually the testing one. Running the test just once is enough, the same things are still covered.
…ed to config, the central config values are kept
Originally, the test only asserted that the broadcasters inherited the correct channel names. Now, the test also asserts that the actual closure and the channel options are inherited too.
…ract (instead of the abstract class) don't break bootstrap() Covers the `$centralBroadcaster instanceof Broadcaster && $tenantBroadcaster instanceof Broadcaster` check in extend(BroadcasterContract::class, ...)
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/Bootstrappers/BroadcastingConfigBootstrapper.php (1)
124-132: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winClear the configuration snapshot after reverting.
$originalConfigsurvivesrevert(). If central broadcasting configuration changes between tenancy sessions, the nextsetConfig()keeps the stale values because of??=, andunsetConfig()later restores those old values instead of the current central configuration.Reset the snapshot after restoring the config.
🛠️ Proposed fix
$this->unsetConfig(); + $this->originalConfig = [];🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/Bootstrappers/BroadcastingConfigBootstrapper.php` around lines 124 - 132, Update revert() in BroadcastingConfigBootstrapper to clear the $originalConfig snapshot after restoring the central broadcasting configuration. Ensure the reset occurs before or alongside the existing unsetConfig() cleanup so the next setConfig() captures current values instead of reusing stale data.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/Bootstrappers/BroadcastingConfigBootstrapper.php`:
- Around line 124-132: Update revert() in BroadcastingConfigBootstrapper to
clear the $originalConfig snapshot after restoring the central broadcasting
configuration. Ensure the reset occurs before or alongside the existing
unsetConfig() cleanup so the next setConfig() captures current values instead of
reusing stale data.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: d36e5e68-49bd-448d-b3b5-cf6cfa38cf76
📒 Files selected for processing (2)
src/Bootstrappers/BroadcastingConfigBootstrapper.phptests/Bootstrappers/BroadcastingConfigBootstrapperTest.php
| // Revert the bound BroadcastManager and Broadcaster singletons back to their original state | ||
| $this->app->singleton(BroadcastManager::class, fn () => $this->originalBroadcastManager); | ||
| $this->app->singleton(BroadcasterContract::class, fn () => $this->originalBroadcaster); |
There was a problem hiding this comment.
| // Revert the bound BroadcastManager and Broadcaster singletons back to their original state | |
| $this->app->singleton(BroadcastManager::class, fn () => $this->originalBroadcastManager); | |
| $this->app->singleton(BroadcasterContract::class, fn () => $this->originalBroadcaster); | |
| // Revert the bound BroadcastManager and Broadcaster singletons back to their original state | |
| $this->app->instance(BroadcastManager::class, $this->originalBroadcastManager); | |
| $this->app->instance(BroadcasterContract::class, $this->originalBroadcaster); |
Would this be equivalent?
| // (which all of Laravel's default broadcasters, e.g. PusherBroadcaster, do). | ||
| if ($centralBroadcaster instanceof Broadcaster && $tenantBroadcaster instanceof Broadcaster) { | ||
| // invade() because channels can't be retrieved through any of the broadcaster's public methods | ||
| $centralBroadcaster = invade($centralBroadcaster); |
There was a problem hiding this comment.
There's a getChannels() method on Broadcaster I think
| $this->app->extend(BroadcastManager::class, function (BroadcastManager $centralManager) { | ||
| $tenantManager = new BroadcastManager($this->app); | ||
|
|
||
| // Pass the custom driver creators registered in the central context to the new manager | ||
| // so that custom drivers work in tenant context without having to re-register the creators manually. | ||
| foreach (invade($centralManager)->customCreators as $driver => $creator) { | ||
| $tenantManager->extend($driver, $creator); | ||
| } | ||
|
|
||
| return $tenantManager; | ||
| }); |
There was a problem hiding this comment.
Is the reason for creating the instance manually like this that if we simply forgot the bound instance from the container, the custom creators that were already registered would be lost?
To confirm - when would BroadcastManager::extend() be used? Is this change simply because of how we do things in our tests or would real applications also extend the manager before tenancy bootstrap?
And what are some common use cases.
Broadcasting auth route problem (
Broadcastfacade always uses centralBroadcastManager)When using
BroadcastingConfigBootstrapperfor broadcasting on private channels with multiple broadcasting apps (= each tenant has its own Pusher/Reverb/... app and credentials), the auth requests sent to the broadcasting server use the central broadcasting credentials. This is because theBroadcastfacade (used inBroadcastController::authenticatelikeBroadcast::auth($request)to authorize the channels and then retrieve the auth key that's then sent to the broadcasting server) keeps using theBroadcastManagerinstance resolved in the central context instead of the tenantBroadcastManager. CallingwithBroadcasting()inbootstrap/app.phpresults in callingBroadcast::routes()in the central context, which resolves and stores the centralBroadcastManager(which is never cleared, and is used in tenant context).Clearing the facade's resolved instance (
Illuminate\Contracts\Broadcasting\Factory) inBroadcastingConfigBootstrapper::bootstrap()forces the facade to re-resolveFactory(=BroadcastManager) on the next use, so the nextBroadcast::auth()call uses the tenantBroadcastManagerin the tenant context (and clearing the resolved instance inrevert()makes theBroadcastfacade use the centralBroadcastManageragain), and the credentials from the current config will be used for authentication.Central
BroadcastManagerdoesn't pass custom driver creators to the tenant managerWhen registering a custom driver creator (
app(BroadcastManager::class)->extend('custom-driver', fn($app, $config) => new CustomBroadcaster(...))) in the central context (e.g. inBroadcastServiceProvider/AppServiceProvider, or in our case, in the tests), the creator won't be available in the tenant context. Calling e.g.app(BroadcastManager::class)->driver('custom-driver')in the tenant context (if the creator was registered only in the central context) would throwInvalidArgumentException("Driver [custom-driver] is not supported.").To fix that, register the original (central)
BroadcastManager's custom creators in the newBroadcastManagerinBroadcastingConfigBootstrapper::bootstrap().Central
Broadcasterinstance bound in tenant contextThe problem was that resolving
Illuminate\Contracts\Broadcasting\Broadcaster::classin tenant context returned the broadcaster instance from the central context (Laravel binds the contract as a singleton, resolved from the default driver's config as it was at resolution time -- before tenancy was initialized). Fixed by makingBroadcaster::classresolve to the currentBroadcastManager's default broadcaster (= the tenant broadcaster in tenant context) viaapp->extend().Since the manager caches its broadcasters (see the next section),
app(Broadcaster::class)andBroadcast::driver()return the same instance in tenant context (consistent with how Laravel behaves in the central context).Broadcasters are resolved once per tenancy initialization,
TenancyBroadcastManageris removedOriginally,
TenancyBroadcastManagerre-resolved the broadcasters listed in its$tenantBroadcastersstatic property on every retrieval. Earlier, we considered that good since it meant direct config changes in tenant context were picked up immediately. As discussed in the review, there's probably no real use case for that, and the re-resolving actually caused a subtle bug: channels registered viaBroadcast::channel()in tenant context got silently lost on the next retrieval (e.g. during a/broadcasting/authrequest), because each re-resolution started over from a copy of the central channels -- making the auth request fail with a 403 as if the channel was never registered.Now,
TenancyBroadcastManageris removed entirely.BroadcastingConfigBootstrapperbinds a fresh, baseBroadcastManagerwith no cached broadcasters, so the broadcasters get resolved using the tenant's credentials on first use and stay cached (like in the parent manager) for the duration of the tenant's context. The central broadcaster's channel auth closures are passed to the tenant broadcaster directly in theBroadcastercontract'sextend()closure -- since Laravel only ever uses the default broadcaster's channel auth closures for broadcasting auth (bothBroadcast::channel()andBroadcast::auth()go through the default broadcaster), the closures only have to be passed to the default broadcaster. This all means that:TenancyBroadcastManageris removed. Its$tenantBroadcastersproperty's purpose was listing the broadcasters to re-resolve (and pass the central channel auth closures to) -- custom drivers now work without the need to configure anything, and the closure copying is a few lines in the bootstrapper instead of a manager override.Broadcast::channel()in tenant context persist for the duration of that context. They don't leak into other tenants' contexts or into the central context.bootstrap(). An already-injected (= stale)Broadcasterinstance additionally needs to be obtained again after reinitialization -- reinitializing swaps the binding, so a previously injected instance keeps using the old credentials.NOTE: Updating credentials affects connected clients
Updating a tenant's credentials also affects already-connected clients (tested with Reverb) -- they're connected using the old key, so they won't receive broadcasts sent with the new credentials.
The frontend has to reconnect using the new key (e.g. by refreshing the page). Meaning, clients can't be notified in response to broadcasting credential changes through websockets themselves -- a broadcast sent after the update already uses the new credentials, so it won't reach clients connected with the old key. So for things like notifying clients in response to the credential changes, a different mechanism is needed.
Credential map: overriding presets (BroadcastingConfigBootstrapper)
Previously, credential mappings from
$mapPresetsoverrode mappings defined in$credentialsMap. If someone used e.g. Pusher and wanted to override some of that preset's mappings, e.g. use 'pusher_app_key' instead of 'pusher_key' by specifying 'pusher_app_key' in$credentialsMap, the preset's mapping ('pusher_key') would still be used.Fixed that by reversing the
array_merge()order inBroadcastingConfigBootstrapper::__construct().Tests
Deleted the BroadcastingTest file, moved the tests to appropriate bootstrapper test files.
Added tests for mapping tenant properties to broadcaster credentials (including keeping the central config values when a tenant doesn't have a mapped property). Also added tests for the rest of the changes mentioned above (including a regression test for the tenant-context channel registration bug), plus tests for copying the channel options along with the auth closures, for broadcasters that only implement the
Broadcastercontract (instead of extending the abstract class), and for configuring which map preset is used via the$broadcasterproperty. Improved the existing tests.Summary by CodeRabbit
Summary by CodeRabbit
Bug Fixes
Tests