Skip to content

Commit

Permalink
[BUG]: upsert() always uses default tenant/database (#3387)
Browse files Browse the repository at this point in the history
  • Loading branch information
codetheweb authored Jan 2, 2025
1 parent e30429b commit 92152f1
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 3 deletions.
6 changes: 4 additions & 2 deletions chromadb/api/models/Collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,8 @@ def upsert(
metadatas=upsert_request["metadatas"],
documents=upsert_request["documents"],
uris=upsert_request["uris"],
tenant=self.tenant,
database=self.database,
)

def delete(
Expand Down Expand Up @@ -383,7 +385,6 @@ def delete(
)



class CollectionName(str):
"""
A string wrapper to supply users with indicative message about list_collections only
Expand All @@ -408,7 +409,8 @@ class CollectionName(str):

def __getattr__(self, item):
collection_attributes_and_methods = [
member for member, _ in inspect.getmembers(Collection)
member
for member, _ in inspect.getmembers(Collection)
if not member.startswith("_")
]

Expand Down
Empty file added chromadb/test/__init__.py
Empty file.
Empty file.
89 changes: 89 additions & 0 deletions chromadb/test/client/test_database_tenant_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import os
from typing import Dict, cast
from fastapi import HTTPException
from overrides import override
import chromadb
from chromadb.api import ServerAPI
from chromadb.auth import (
AuthzAction,
AuthzResource,
ServerAuthenticationProvider,
ServerAuthorizationProvider,
UserIdentity,
)
from chromadb.config import Settings, System
from chromadb.test.conftest import _fastapi_fixture
from hypothesis.stateful import (
run_state_machine_as_test,
)
from chromadb.test.property.test_embeddings import EmbeddingStateMachine


class ExampleAuthenticationProvider(ServerAuthenticationProvider):
"""In practice the tenant would likely be resolved from some other opaque value (e.g. key/token). Here, it's just passed directly as a header for simplicity."""

@override
def authenticate_or_raise(self, headers: Dict[str, str]) -> UserIdentity:
return UserIdentity(
user_id="test",
tenant=headers.get("x-tenant", None),
)


class ExampleAuthorizationProvider(ServerAuthorizationProvider):
"""A simple authz provider that asserts the user's tenant matches the resource's tenant."""

def __init__(self, system: System) -> None:
super().__init__(system)
self._settings = system.settings

@override
def authorize_or_raise(
self, user: UserIdentity, action: AuthzAction, resource: AuthzResource
) -> None:
if user.tenant is None:
return

if action == AuthzAction.RESET:
return

if user.tenant != resource.tenant:
raise HTTPException(status_code=403, detail="Unauthorized")


def test_tenant_and_database_passed_from_client() -> None:
if os.environ.get("CHROMA_INTEGRATION_TEST_ONLY"):
host = os.environ.get("CHROMA_SERVER_HOST", "localhost")
port = int(os.environ.get("CHROMA_SERVER_HTTP_PORT", 0))

settings = Settings()
settings.chroma_server_http_port = port
settings.chroma_server_host = host
admin_client = chromadb.AdminClient(settings)
admin_client.create_tenant("test_tenant")
admin_client.create_database("test_database", "test_tenant")
else:
api_fixture = _fastapi_fixture(
chroma_server_authn_provider="chromadb.test.client.test_database_tenant_auth.ExampleAuthenticationProvider",
chroma_server_authz_provider="chromadb.test.client.test_database_tenant_auth.ExampleAuthorizationProvider",
)
sys: System = next(api_fixture)
sys.reset_state()

server = sys.require(ServerAPI)
server.create_tenant("test_tenant")
server.create_database("test_database", "test_tenant")
host = cast(str, sys.settings.chroma_server_host)
port = cast(int, sys.settings.chroma_server_http_port)

client = chromadb.HttpClient(
host=host,
port=port,
headers={"x-tenant": "test_tenant"},
tenant="test_tenant",
database="test_database",
)

run_state_machine_as_test(
lambda: EmbeddingStateMachine(client),
) # type: ignore
1 change: 0 additions & 1 deletion chromadb/test/property/test_embeddings.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ def __init__(self, client: ClientAPI):

@initialize(collection=collection_st) # type: ignore
def initialize(self, collection: strategies.Collection):
reset(self.client)
self.collection = self.client.create_collection(
name=collection.name,
metadata=collection.metadata, # type: ignore[arg-type]
Expand Down

0 comments on commit 92152f1

Please sign in to comment.