fix(pgwire): scope catalog resolution and trust auth to the connecting tenant#32
Merged
farhan-syah merged 2 commits intomainfrom Apr 15, 2026
Merged
Conversation
…g tenant QueryContext was constructed once per handler with a fixed tenant id (always 1), causing every SQL plan built during a session to resolve collection metadata against tenant 1 regardless of which tenant the connecting user belonged to. Remove tenant_id from QueryContext / CatalogInputs construction and instead pass it through to build_adapter() at plan time, so each plan_sql() call uses the tenant derived from the authenticated identity on that connection. The same hardcoded-1 pattern was present in the HTTP server, native session, event trigger dispatcher, procedural executor, scatter-gather planner, CDC consume path, and topic publish path — all updated to use the tenant-free construction form. Fix Trust-mode authentication to perform strict identity resolution rather than silently admitting any username as a tenant-1 superuser. Implement NoopStartupHandler::post_startup to reject unknown users with SQLSTATE 28000, matching PostgreSQL trust-method semantics. A one-time bootstrap exception admits and persists the first connecting user when the credential store is empty, so fresh deployments do not require out-of-band provisioning. Fix parse-time catalog lookup in NodeDbQueryParser to derive tenant_id from the connecting user's pgwire metadata rather than always using tenant 1, so Describe responses for prepared statements include field info from the correct tenant's schema.
…and trust auth Cover the two regression classes fixed in the preceding commit: - Tenant-scoped users can only see and modify collections owned by their own tenant; a query against a collection belonging to another tenant returns a resolution error, not results from the wrong catalog. - Trust mode rejects usernames that were never provisioned with SQLSTATE 28000 rather than silently admitting them as superusers. Extend pgwire_harness::TestServer with connect_as() to open additional connections under arbitrary credentials, and pre-provision the default "nodedb" superuser so the bootstrap exception does not fire during tests that subsequently create additional users.
This was referenced Apr 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #29
Closes #30
Summary
QueryContextat construction with a hardcodedtenant_id = 1, and the prepared-statement parser did the same. Every query from a tenant > 1 user planned against tenant 1's catalog and failed withunknown table, even on a collection the same connection had just created. Fixed by removing the tenant fromCatalogInputsentirely and threading it per plan call viabuild_adapter(tenant_id); parser.rs now resolves the connecting user's tenant from pgwire metadata before building its catalog.resolve_identityfabricated a tenant-1 superuser for any unknown username. Fixed by (a) rejecting unknown users at connect time viaNoopStartupHandler::post_startupwith SQLSTATE28000, (b) removing the silent-superuser fallback inresolve_identity, and (c) keeping the empty-store bootstrap path but persisting the first connecting user so subsequent queries resolve strictly.The root causes were localised to three files (
planner/context.rs,pgwire/handler/core.rs,pgwire/handler/prepared/parser.rs) pluspgwire/factory.rsfor the auth audit path. All downstreamQueryContextconstructors (for_state,for_state_with_lease,with_catalog) lost their now-meaninglesstenant_idparameter; 14 call sites updated.Tests
New wire-level test file
nodedb/tests/pgwire_tenant_scoping.rs— 8 tests covering the full class of bug, not just the reported symptom:SELECT/INSERT/UPDATE/DELETEon own collectionprepareagainst own collection (extended protocol →parser.rspath)SELECTagainst a tenant-1 collection must surfaceunknown table, not silently return an empty result (asymmetric-isolation guard)Harness additions in
tests/common/pgwire_harness.rs:pub pg_portandconnect_as(user, password)so multiple concurrent connections under different usernames can share one listener.Test plan
cargo nextest run -p nodedb --test pgwire_tenant_scoping— 8/8 passcargo nextest run -p nodedb— 2827/2827 passcargo clippy -p nodedb --all-targets -- -D warnings— cleancargo fmt --all— clean