fix(adapter): write permission rows with the document's tenant on update - #952
Conversation
createDocument binds the document's own tenant for the rows it writes to the _perms side table; updateDocument bound the adapter's. The two only diverge for a row whose tenant is not the selected one, which on a shared pool is the null-tenant _metadata row of a collection created once for every tenant: getTenantQuery() carves out `OR _tenant IS NULL` for Database::METADATA, so such a row is readable and updatable with any tenant selected. Updating one under a tenant therefore left the document row on tenant NULL and its permission rows on that tenant. Permission filtering reads those rows, so the definition became invisible to every other tenant on the pool. SQLite's own updateDocument went further and rewrote the document row's _tenant column to the selected tenant, moving the row off the shared scope entirely. Postgres is fixed for consistency with its createDocument; its permission filtering reads the denormalised _permissions column rather than the side table, so the regression test cannot observe the difference there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Warning Review limit reachedNext included review available in 53 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe SQL adapters now use each document’s tenant for shared-table updates and permission deletion or insertion. An end-to-end test verifies that tenantless shared metadata remains visible across tenants after an update. ChangesShared tenant permissions
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: ⚪ Minimal · up to The update keeps shared documents and their permission rows in the document's existing tenant scope across the affected SQL adapters. No actionable merge-blocking risk remains after normal checks and review. Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 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 |
Greptile SummaryFixes SQL shared-table updates so document rows and permission side-table rows retain the persisted document tenant.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
Reviews (2): Last reviewed commit: "Apply suggestions from code review" | Re-trigger Greptile |
Co-authored-by: Jake Barnby <jakeb994@gmail.com>
Brings in #952, the shared-definition permission tenant fix. main bound permission rows to $document->getTenant() instead of $this->tenant across three adapters. This branch moved that write into Hook\Permissions, and the insert side already passes the document's tenant through documentMetadata(), so the visibility half of the fix is present: the facade stamps $document[TENANT] from the stored row ("Make sure user doesn't switch tenant"), which is NULL for a shared definition, so its permission rows stay tenantless and every tenant on the pool keeps reading it. main's test is carried over, converted to the Collection value object, and asserts exactly that. The delete half was missing. deletePermissions() builds its query through newBuilder(), which attaches a TenantFilter scoped to the adapter's tenant, and that filter only relaxes to "IN (?) OR IS NULL" when the table name equals the metadata collection. It is handed the physical table, so '_metadata_perms' was compared against '_metadata', never matched, and the side table was filtered strictly to the writer's tenant. Revoking a permission on a shared definition matched no rows and silently did nothing. A metadata row may be tenantless, so its permission rows may be too, and the side table now resolves as metadata. Non-metadata permission tables stay strictly tenanted -- both are pinned, and the metadata case was seen red as 'perms._tenant IN (?)' with no IS NULL. 1563 unit tests, PHPStan and Pint clean. The e2e case needs a redis host and runs in CI. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The bug
createDocumentbinds the document's own tenant for the rows it writes to the_permsside table.updateDocumentbound the adapter's:The two only diverge for a row whose tenant is not the selected one. On a shared pool that is the null-tenant
_metadatarow of a collection created once for every tenant:getTenantQuery()carves outOR _tenant IS NULLwhen the collection isDatabase::METADATA, so such a row is readable and updatable with any tenant selected, andDatabase::updateDocumentdeliberately forces$document['$tenant'] = $old->getTenant()to keep it where it is.Updating one under a tenant left the document row on
NULLand its permission rows on that tenant:Permission filtering reads those rows (
getSQLPermissionsConditionsubqueries_perms), so the shared definition became invisible to every other tenant on the pool — the same repro reportspermission-filtered find as an unrelated tenant: NOT FOUND.SQLite went further: its own
updateDocumentoverride rewrote the document row's_tenantcolumn itself ($attributes['_tenant'] = $this->tenant), moving the row off the shared scope entirely.The fix
All three write sites use
$document->getTenant(), matchingcreateDocument. For every ordinary document this is a no-op —$oldis fetched under the tenant query, so its tenant already equals the adapter's.Postgres is fixed for consistency with its own
createDocument. Its permission filtering reads the denormalised_permissionsJSONB column rather than the side table, so the regression test cannot observe the difference there — stated plainly because that adapter's change is not test-covered.Testing
testUpdatingASharedDefinitionKeepsItsPermissionRowsTenantlessin the sharedPermissionTestsscope, guarded to shared-tables runs onSQLadapters (Mongo keeps permissions on the document, so it cannot have this bug).Seen red before green:
_permissions, cannot observe)Full
tests/e2e/Adapter/SharedTablessuites pass on all four: 672 tests each.composer lintalready fails onmainfor untouched files (Mongo.php,Redis.php); the four files here report the same violations before and after this change.Follow-up, not changed here
SQLite::createDocumentalso uses$this->tenantfor the document row whereMariaDB::createDocumentuses$document->getTenant()(src/Database/Adapter/SQLite.php:1155). Same class of divergence, but nothing here exercises it, so it is left for a change that can prove it.🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests