Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
__init__.py238 linesDownload Raw Back to auth
1from __future__ import annotations
2
3from abc import abstractmethod
4from enum import Enum
5from typing import (
6    Any,
7    List,
8    Optional,
9    Dict,
10    Tuple,
11    TypeVar,
12)
13from dataclasses import dataclass
14
15from pydantic import SecretStr
16
17from chromadb.config import (
18    Component,
19    System,
20)
21
22T = TypeVar("T")
23S = TypeVar("S")
24
25
26class AuthError(Exception):
27    pass
28
29
30ClientAuthHeaders = Dict[str, SecretStr]
31
32
33class ClientAuthProvider(Component):
34    """
35    ClientAuthProvider is responsible for providing authentication headers for
36    client requests. Client implementations (in our case, just the FastAPI
37    client) must inject these headers into their requests.
38    """
39
40    def __init__(self, system: System) -> None:
41        super().__init__(system)
42
43    @abstractmethod
44    def authenticate(self) -> ClientAuthHeaders:
45        pass
46
47
48@dataclass
49class UserIdentity:
50    """
51    UserIdentity represents the identity of a user. In general, not all fields
52    will be populated, and the fields that are populated will depend on the
53    authentication provider.
54
55    The idea is that the AuthenticationProvider is responsible for populating
56    _all_ information known about the user, and the AuthorizationProvider is
57    responsible for making decisions based on that information.
58    """
59
60    user_id: str
61    tenant: Optional[str] = None
62    databases: Optional[List[str]] = None
63    # This can be used for any additional auth context which needs to be
64    # propagated from the authentication provider to the authorization
65    # provider.
66    attributes: Optional[Dict[str, Any]] = None
67
68
69class ServerAuthenticationProvider(Component):
70    """
71    ServerAuthenticationProvider is responsible for authenticating requests. If
72    a ServerAuthenticationProvider is configured, it will be called by the
73    server to authenticate requests. If no ServerAuthenticationProvider is
74    configured, all requests will be authenticated.
75
76    The ServerAuthenticationProvider should return a UserIdentity object if the
77    request is authenticated for use by the ServerAuthorizationProvider.
78    """
79
80    def __init__(self, system: System) -> None:
81        super().__init__(system)
82        self._ignore_auth_paths: Dict[
83            str, List[str]
84        ] = system.settings.chroma_server_auth_ignore_paths
85        self.overwrite_singleton_tenant_database_access_from_auth = (
86            system.settings.chroma_overwrite_singleton_tenant_database_access_from_auth
87        )
88
89    @abstractmethod
90    def authenticate_or_raise(self, headers: Dict[str, str]) -> UserIdentity:
91        pass
92
93    def ignore_operation(self, verb: str, path: str) -> bool:
94        if (
95            path in self._ignore_auth_paths.keys()
96            and verb.upper() in self._ignore_auth_paths[path]
97        ):
98            return True
99        return False
100
101    def read_creds_or_creds_file(self) -> List[str]:
102        _creds_file = None
103        _creds = None
104
105        if self._system.settings.chroma_server_authn_credentials_file:
106            _creds_file = str(
107                self._system.settings["chroma_server_authn_credentials_file"]
108            )
109        if self._system.settings.chroma_server_authn_credentials:
110            _creds = str(self._system.settings["chroma_server_authn_credentials"])
111        if not _creds_file and not _creds:
112            raise ValueError(
113                "No credentials file or credentials found in "
114                "[chroma_server_authn_credentials]."
115            )
116        if _creds_file and _creds:
117            raise ValueError(
118                "Both credentials file and credentials found."
119                "Please provide only one."
120            )
121        if _creds:
122            return [c for c in _creds.split("\n") if c]
123        elif _creds_file:
124            with open(_creds_file, "r") as f:
125                return f.readlines()
126        raise ValueError("Should never happen")
127
128    def singleton_tenant_database_if_applicable(
129        self, user: Optional[UserIdentity]
130    ) -> Tuple[Optional[str], Optional[str]]:
131        """
132        If settings.chroma_overwrite_singleton_tenant_database_access_from_auth
133        is False, this function always returns (None, None).
134
135        If settings.chroma_overwrite_singleton_tenant_database_access_from_auth
136        is True, follows the following logic:
137        - If the user only has access to a single tenant, this function will
138          return that tenant as its first return value.
139        - If the user only has access to a single database, this function will
140          return that database as its second return value. If the user has
141          access to multiple tenants and/or databases, including "*", this
142          function will return None for the corresponding value(s).
143        - If the user has access to multiple tenants and/or databases this
144          function will return None for the corresponding value(s).
145        """
146        if not self.overwrite_singleton_tenant_database_access_from_auth or not user:
147            return None, None
148        tenant = None
149        database = None
150        if user.tenant and user.tenant != "*":
151            tenant = user.tenant
152        if user.databases and len(user.databases) == 1 and user.databases[0] != "*":
153            database = user.databases[0]
154        return tenant, database
155
156
157class AuthzAction(str, Enum):
158    """
159    The set of actions that can be authorized by the authorization provider.
160    """
161
162    RESET = "system:reset"
163    CREATE_TENANT = "tenant:create_tenant"
164    GET_TENANT = "tenant:get_tenant"
165    CREATE_DATABASE = "db:create_database"
166    GET_DATABASE = "db:get_database"
167    DELETE_DATABASE = "db:delete_database"
168    LIST_DATABASES = "db:list_databases"
169    LIST_COLLECTIONS = "db:list_collections"
170    COUNT_COLLECTIONS = "db:count_collections"
171    CREATE_COLLECTION = "db:create_collection"
172    GET_OR_CREATE_COLLECTION = "db:get_or_create_collection"
173    GET_COLLECTION = "collection:get_collection"
174    DELETE_COLLECTION = "collection:delete_collection"
175    UPDATE_COLLECTION = "collection:update_collection"
176    ADD = "collection:add"
177    DELETE = "collection:delete"
178    GET = "collection:get"
179    QUERY = "collection:query"
180    COUNT = "collection:count"
181    UPDATE = "collection:update"
182    UPSERT = "collection:upsert"
183
184
185@dataclass
186class AuthzResource:
187    """
188    The resource being accessed in an authorization request.
189    """
190
191    tenant: Optional[str]
192    database: Optional[str]
193    collection: Optional[str]
194
195
196class ServerAuthorizationProvider(Component):
197    """
198    ServerAuthorizationProvider is responsible for authorizing requests. If a
199    ServerAuthorizationProvider is configured, it will be called by the server
200    to authorize requests. If no ServerAuthorizationProvider is configured, all
201    requests will be authorized.
202
203    ServerAuthorizationProvider should raise an exception if the request is not
204    authorized.
205    """
206
207    def __init__(self, system: System) -> None:
208        super().__init__(system)
209
210    @abstractmethod
211    def authorize_or_raise(
212        self, user: UserIdentity, action: AuthzAction, resource: AuthzResource
213    ) -> None:
214        pass
215
216    def read_config_or_config_file(self) -> List[str]:
217        _config_file = None
218        _config = None
219        if self._system.settings.chroma_server_authz_config_file:
220            _config_file = self._system.settings["chroma_server_authz_config_file"]
221        if self._system.settings.chroma_server_authz_config:
222            _config = str(self._system.settings["chroma_server_authz_config"])
223        if not _config_file and not _config:
224            raise ValueError(
225                "No authz configuration file or authz configuration found."
226            )
227        if _config_file and _config:
228            raise ValueError(
229                "Both authz configuration file and authz configuration found."
230                "Please provide only one."
231            )
232        if _config:
233            return [c for c in _config.split("\n") if c]
234        elif _config_file:
235            with open(_config_file, "r") as f:
236                return f.readlines()
237        raise ValueError("Should never happen")
238 
codekingpro/portable-devtools · Team Ai