Skip to content

Fix initializeCache() not clearing in-memory perms collection on tenant/cache switch - #2965

Merged
drbyte merged 1 commit into
mainfrom
tenant-initialize-cache-issue-2964
Jul 3, 2026
Merged

Fix initializeCache() not clearing in-memory perms collection on tenant/cache switch#2965
drbyte merged 1 commit into
mainfrom
tenant-initialize-cache-issue-2964

Conversation

@drbyte

@drbyte drbyte commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes #2964

Problem:

initializeCache() re-resolves the cache store and config but leaves $this->permissions untouched. Since PermissionRegistrar is a singleton and loadPermissions() short-circuits when that collection is already populated:

if ($this->permissions) {
    return;
}

any process that switches cache context mid-lifetime (multi-tenant apps switching tenants, queue workers, artisan commands looping over tenants, Octane workers) keeps serving the previous tenant's permissions after calling initializeCache()exactly the call our own docs recommend for this scenario. Because hasDirectPermission() matches by primary key, and each tenant DB has independent auto-increment IDs, a leftover collection doesn't just serve stale data — it can resolve permission names to the wrong tenant's IDs and return incorrect authorization results.

Fix:
initializeCache() now also clears the in-memory permissions collection (via clearPermissionsCollection()) and the transient $cachedRoles buffer, so the next permission check rebuilds from the newly-configured cache/tenant instead of reusing stale data.

Changes:

Impact: No behavior change for typical single-tenant apps. initializeCache() is otherwise only called once at registrar construction, where $permissions is already null, so this adds no overhead there. It only changes behavior for the explicit-reinitialize-mid-request pattern our docs already describe, making that pattern actually work as documented.

…nt/cache switch

Fixes #2964

**Problem:**

`initializeCache()` re-resolves the cache store and config but leaves `$this->permissions` untouched. Since `PermissionRegistrar` is a singleton and `loadPermissions()` short-circuits when that collection is already populated:

```php
if ($this->permissions) {
    return;
}
```

any process that switches cache context mid-lifetime (multi-tenant apps switching tenants, queue workers, artisan commands looping over tenants, Octane workers) keeps serving the *previous* tenant's permissions after calling `initializeCache()` — **exactly the call our own docs recommend for this scenario**. Because `hasDirectPermission()` matches by primary key, and each tenant DB has independent auto-increment IDs, a leftover collection doesn't just serve stale data — it can resolve permission names to the wrong tenant's IDs and return incorrect authorization results.

**Fix:**
`initializeCache()` now also clears the in-memory permissions collection (via `clearPermissionsCollection()`) and the transient `$cachedRoles` buffer, so the next permission check rebuilds from the newly-configured cache/tenant instead of reusing stale data.

**Changes:**
- [`src/PermissionRegistrar.php`](src/PermissionRegistrar.php) — `initializeCache()` clears in-memory permissions/roles state.
- [`tests/Integration/PermissionRegistrarTest.php`](tests/Integration/PermissionRegistrarTest.php) — regression test asserting the loaded collection is `null` after `initializeCache()`.
- [`docs/advanced-usage/cache.md`](docs/advanced-usage/cache.md) — clarifies that `initializeCache()` now also discards the loaded collection.

**Impact:** No behavior change for typical single-tenant apps. `initializeCache()` is otherwise only called once at registrar construction, where `$permissions` is already `null`, so this adds no overhead there. It only changes behavior for the explicit-reinitialize-mid-request pattern our docs already describe, making that pattern actually work as documented.
@drbyte
drbyte force-pushed the tenant-initialize-cache-issue-2964 branch from 735bc43 to e80d7b0 Compare July 3, 2026 14:43
@drbyte
drbyte merged commit 396c747 into main Jul 3, 2026
30 checks passed
@drbyte
drbyte deleted the tenant-initialize-cache-issue-2964 branch July 3, 2026 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

initializeCache() doesn't clear loaded permissions, leaking permissions between tenants

1 participant