Skip to content

Enforce tenant isolation via TenantScopedClient + CI guard - #5

Open
AshSgDe29071999 wants to merge 3 commits into
AgentPostmortem:mainfrom
AshSgDe29071999:fix/enforce-tenant-query-wrapper
Open

Enforce tenant isolation via TenantScopedClient + CI guard#5
AshSgDe29071999 wants to merge 3 commits into
AgentPostmortem:mainfrom
AshSgDe29071999:fix/enforce-tenant-query-wrapper

Conversation

@AshSgDe29071999

Copy link
Copy Markdown

Summary

Tenant isolation depended on every query path calling build_filter. A forgotten filter is silent and returns other tenants' documents.

  • Add TenantScopedClient — required tenant_id, always injects tenant filter
  • search() uses the wrapper for dense/sparse/hybrid (incl. Prefetch)
  • CI test greps for raw client.query_points / scroll / retrieve outside the wrapper
  • README documents the mechanism as the part worth copying

search() results unchanged for normal callers.

Test plan

  • pytest (incl. multitenancy + new scoped access tests)
  • Deliberately leaky client.query_points pattern matches the guard

Fixes #3

AshSgDe29071999 and others added 2 commits August 8, 2026 18:23
Isolation used to rest on every call site remembering build_filter.
Route all search reads through a wrapper that requires tenant_id and
always injects the tenant filter (including hybrid Prefetch branches).
A CI test fails if package code calls client.query_points/scroll/retrieve
outside that wrapper. search() behaviour is unchanged for callers.

Fixes AgentPostmortem#3
@AshSgDe29071999

Copy link
Copy Markdown
Author

Fixed ruff I001/F401 in tests (import sorting + unused qdrant_client.models).

@AshSgDe29071999

Copy link
Copy Markdown
Author

Fixed remaining CI ruff I001: first-party imports in src/tenantq/search.py sorted (scoped_client after metrics).

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.

Tenant isolation rests on every query path remembering to call build_filter, with nothing enforcing it

1 participant