search_documents requires a workspace (no more filter: None over the shared collection)
Ingest writes company_id metadata (retrieval still keys on workspace_id for back-compat)
Legacy get_retriever always applies a workspace filter
Test plan
manage.py test chat_backend.tests.test_chat_tenant_scope chat_backend.tests.test_consumers chat_backend.tests.test_services_rag chat_backend.tests.test_views_conversations
Manual: user A cannot stream RAG context from user B conversation_id
Manual: RAG still returns own-company docs after deploy (existing vectors with workspace_id only)
Follow-up: FE can send JWT token/access on WS payloads for stronger identity binding
## Summary
- Closes [#40](https://git.aimloperations.com/ai_ml_operations/chat_backend/issues/40)
- Aligns chat/RAG with the abc_worker stove-pipe pattern ([b13cec8](https://git.aimloperations.com/GIS/abc_worker/commit/b13cec88f9730289f245b66b95aa11ee33cb4e8e)): immutable `ChatCompanyScope` per turn, conversation ownership validation, fail-closed Chroma filters
- Prefer ASGI/JWT identity over client email; never bind identity from `conversation_id` alone
- Close `ConversationDetailView` IDOR (prompts only for `request.user`)
## Changes
- New `services/chat_tenant_scope.py` with frozen `ChatCompanyScope` + ownership checks
- WebSocket consumers (`consumers.py` / `consumers_graph.py`) validate scope before `get_messages` / RAG
- `search_documents` requires a workspace (no more `filter: None` over the shared collection)
- Ingest writes `company_id` metadata (retrieval still keys on `workspace_id` for back-compat)
- Legacy `get_retriever` always applies a workspace filter
## Test plan
- [x] `manage.py test chat_backend.tests.test_chat_tenant_scope chat_backend.tests.test_consumers chat_backend.tests.test_services_rag chat_backend.tests.test_views_conversations`
- [ ] Manual: user A cannot stream RAG context from user B `conversation_id`
- [ ] Manual: RAG still returns own-company docs after deploy (existing vectors with `workspace_id` only)
- [ ] Follow-up: FE can send JWT `token`/`access` on WS payloads for stronger identity binding
Validate conversation ownership before workspace resolution, fail closed
when Chroma filters are missing, and close ConversationDetailView IDOR so
tenants cannot pull another company's document chunks.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
ChatCompanyScopeper turn, conversation ownership validation, fail-closed Chroma filtersconversation_idaloneConversationDetailViewIDOR (prompts only forrequest.user)Changes
services/chat_tenant_scope.pywith frozenChatCompanyScope+ ownership checksconsumers.py/consumers_graph.py) validate scope beforeget_messages/ RAGsearch_documentsrequires a workspace (no morefilter: Noneover the shared collection)company_idmetadata (retrieval still keys onworkspace_idfor back-compat)get_retrieveralways applies a workspace filterTest plan
manage.py test chat_backend.tests.test_chat_tenant_scope chat_backend.tests.test_consumers chat_backend.tests.test_services_rag chat_backend.tests.test_views_conversationsconversation_idworkspace_idonly)token/accesson WS payloads for stronger identity binding