mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-01 17:39:39 +02:00
Per-workspace queue routing for workspace-scoped services (#862)
Workspace identity is now determined by queue infrastructure instead of message body fields, closing a privilege-escalation vector where a caller could spoof workspace in the request payload. - Add WorkspaceProcessor base class: discovers workspaces from config at startup, creates per-workspace consumers (queue:workspace), and manages consumer lifecycle on workspace create/delete events - Roll out to librarian, flow-svc, knowledge cores, and config-svc - Config service gets a dual-queue regime: a system queue for cross-workspace ops (getvalues-all-ws, bootstrapper writes to __workspaces__) and per-workspace queues for tenant-scoped ops, with workspace discovery from its own Cassandra store - Remove workspace field from request schemas (FlowRequest, LibrarianRequest, KnowledgeRequest, CollectionManagementRequest) and from DocumentMetadata / ProcessingMetadata — table stores now accept workspace as an explicit parameter - Strip workspace encode/decode from all message translators and gateway serializers - Gateway enforces workspace existence: reject requests targeting non-existent workspaces instead of routing to queues with no consumer - Config service provisions new workspaces from __template__ on creation - Add workspace lifecycle hooks to AsyncProcessor so any processor can react to workspace create/delete without subclassing WorkspaceProcessor
This commit is contained in:
parent
9be257ceee
commit
9f2bfbce0c
53 changed files with 1565 additions and 677 deletions
|
|
@ -9,7 +9,6 @@ class CollectionManagementRequestTranslator(MessageTranslator):
|
|||
def decode(self, data: Dict[str, Any]) -> CollectionManagementRequest:
|
||||
return CollectionManagementRequest(
|
||||
operation=data.get("operation"),
|
||||
workspace=data.get("workspace", ""),
|
||||
collection=data.get("collection"),
|
||||
timestamp=data.get("timestamp"),
|
||||
name=data.get("name"),
|
||||
|
|
@ -24,8 +23,6 @@ class CollectionManagementRequestTranslator(MessageTranslator):
|
|||
|
||||
if obj.operation is not None:
|
||||
result["operation"] = obj.operation
|
||||
if obj.workspace:
|
||||
result["workspace"] = obj.workspace
|
||||
if obj.collection is not None:
|
||||
result["collection"] = obj.collection
|
||||
if obj.timestamp is not None:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ class FlowRequestTranslator(MessageTranslator):
|
|||
def decode(self, data: Dict[str, Any]) -> FlowRequest:
|
||||
return FlowRequest(
|
||||
operation=data.get("operation"),
|
||||
workspace=data.get("workspace", ""),
|
||||
blueprint_name=data.get("blueprint-name"),
|
||||
blueprint_definition=data.get("blueprint-definition"),
|
||||
description=data.get("description"),
|
||||
|
|
@ -22,8 +21,6 @@ class FlowRequestTranslator(MessageTranslator):
|
|||
|
||||
if obj.operation is not None:
|
||||
result["operation"] = obj.operation
|
||||
if obj.workspace is not None:
|
||||
result["workspace"] = obj.workspace
|
||||
if obj.blueprint_name is not None:
|
||||
result["blueprint-name"] = obj.blueprint_name
|
||||
if obj.blueprint_definition is not None:
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ class KnowledgeRequestTranslator(MessageTranslator):
|
|||
|
||||
return KnowledgeRequest(
|
||||
operation=data.get("operation"),
|
||||
workspace=data.get("workspace", ""),
|
||||
id=data.get("id"),
|
||||
flow=data.get("flow"),
|
||||
collection=data.get("collection"),
|
||||
|
|
@ -58,8 +57,6 @@ class KnowledgeRequestTranslator(MessageTranslator):
|
|||
|
||||
if obj.operation:
|
||||
result["operation"] = obj.operation
|
||||
if obj.workspace:
|
||||
result["workspace"] = obj.workspace
|
||||
if obj.id:
|
||||
result["id"] = obj.id
|
||||
if obj.flow:
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ class LibraryRequestTranslator(MessageTranslator):
|
|||
document_metadata=doc_metadata,
|
||||
processing_metadata=proc_metadata,
|
||||
content=content,
|
||||
workspace=data.get("workspace", ""),
|
||||
collection=data.get("collection", ""),
|
||||
criteria=criteria,
|
||||
# Chunked upload fields
|
||||
|
|
@ -76,8 +75,6 @@ class LibraryRequestTranslator(MessageTranslator):
|
|||
result["processing-metadata"] = self.proc_metadata_translator.encode(obj.processing_metadata)
|
||||
if obj.content:
|
||||
result["content"] = obj.content.decode("utf-8") if isinstance(obj.content, bytes) else obj.content
|
||||
if obj.workspace:
|
||||
result["workspace"] = obj.workspace
|
||||
if obj.collection:
|
||||
result["collection"] = obj.collection
|
||||
if obj.criteria is not None:
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ class DocumentMetadataTranslator(Translator):
|
|||
title=data.get("title"),
|
||||
comments=data.get("comments"),
|
||||
metadata=self.subgraph_translator.decode(metadata) if metadata is not None else [],
|
||||
workspace=data.get("workspace"),
|
||||
tags=data.get("tags"),
|
||||
parent_id=data.get("parent-id", ""),
|
||||
document_type=data.get("document-type", "source"),
|
||||
|
|
@ -40,8 +39,6 @@ class DocumentMetadataTranslator(Translator):
|
|||
result["comments"] = obj.comments
|
||||
if obj.metadata is not None:
|
||||
result["metadata"] = self.subgraph_translator.encode(obj.metadata)
|
||||
if obj.workspace:
|
||||
result["workspace"] = obj.workspace
|
||||
if obj.tags is not None:
|
||||
result["tags"] = obj.tags
|
||||
if obj.parent_id:
|
||||
|
|
@ -61,7 +58,6 @@ class ProcessingMetadataTranslator(Translator):
|
|||
document_id=data.get("document-id"),
|
||||
time=data.get("time"),
|
||||
flow=data.get("flow"),
|
||||
workspace=data.get("workspace"),
|
||||
collection=data.get("collection"),
|
||||
tags=data.get("tags")
|
||||
)
|
||||
|
|
@ -77,8 +73,6 @@ class ProcessingMetadataTranslator(Translator):
|
|||
result["time"] = obj.time
|
||||
if obj.flow:
|
||||
result["flow"] = obj.flow
|
||||
if obj.workspace:
|
||||
result["workspace"] = obj.workspace
|
||||
if obj.collection:
|
||||
result["collection"] = obj.collection
|
||||
if obj.tags is not None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue