11from __future__ import annotations
22
3- import string
43from collections .abc import Mapping
54from dataclasses import asdict , dataclass
65from io import BytesIO
1211import zstandard
1312from urllib3 .connectionpool import HTTPConnectionPool
1413
14+ from objectstore_client .auth import TokenGenerator
1515from objectstore_client .metadata import (
1616 HEADER_EXPIRATION ,
1717 HEADER_META_PREFIX ,
2525 NoOpMetricsBackend ,
2626 measure_storage_operation ,
2727)
28-
29- Permission = Literal ["read" , "write" ]
28+ from objectstore_client .scope import Scope
3029
3130
3231class GetResponse (NamedTuple ):
@@ -68,12 +67,6 @@ def __init__(
6867 self ._expiration_policy = expiration_policy
6968
7069
71- # Characters allowed in a Scope's key and value.
72- # These are the URL safe characters, except for `.` which we use as separator between
73- # key and value of Scope components.
74- SCOPE_VALUE_ALLOWED_CHARS = set (string .ascii_letters + string .digits + "-_()$!+'" )
75-
76-
7770@dataclass
7871class _ConnectionDefaults :
7972 retries : urllib3 .Retry = urllib3 .Retry (connect = 3 , read = 0 )
@@ -100,6 +93,7 @@ def __init__(
10093 retries : int | None = None ,
10194 timeout_ms : float | None = None ,
10295 connection_kwargs : Mapping [str , Any ] | None = None ,
96+ token_generator : TokenGenerator | None = None ,
10397 ):
10498 connection_kwargs_to_use = asdict (_ConnectionDefaults ())
10599
@@ -125,11 +119,12 @@ def __init__(
125119 self ._base_path = urlparse (base_url ).path
126120 self ._metrics_backend = metrics_backend or NoOpMetricsBackend ()
127121 self ._propagate_traces = propagate_traces
122+ self ._token_generator = token_generator
128123
129124 def session (self , usecase : Usecase , ** scopes : str | int | bool ) -> Session :
130125 """
131126 Create a [Session] with the Objectstore server, tied to a specific [Usecase] and
132- Scope.
127+ [ Scope] .
133128
134129 A Scope is a (possibly nested) namespace within a Usecase, given as a sequence
135130 of key-value pairs passed as kwargs.
@@ -148,37 +143,14 @@ def session(self, usecase: Usecase, **scopes: str | int | bool) -> Session:
148143 ```
149144 """
150145
151- parts = []
152- for key , value in scopes .items ():
153- if not key :
154- raise ValueError ("Scope key cannot be empty" )
155- if not value :
156- raise ValueError ("Scope value cannot be empty" )
157-
158- if any (c not in SCOPE_VALUE_ALLOWED_CHARS for c in key ):
159- raise ValueError (
160- f"Invalid scope key { key } . The valid character set is: "
161- f"{ '' .join (SCOPE_VALUE_ALLOWED_CHARS )} "
162- )
163-
164- value = str (value )
165- if any (c not in SCOPE_VALUE_ALLOWED_CHARS for c in value ):
166- raise ValueError (
167- f"Invalid scope value { value } . The valid character set is: "
168- f"{ '' .join (SCOPE_VALUE_ALLOWED_CHARS )} "
169- )
170-
171- formatted = f"{ key } ={ value } "
172- parts .append (formatted )
173- scope_str = ";" .join (parts )
174-
175146 return Session (
176147 self ._pool ,
177148 self ._base_path ,
178149 self ._metrics_backend ,
179150 self ._propagate_traces ,
180151 usecase ,
181- scope_str ,
152+ Scope (** scopes ),
153+ self ._token_generator ,
182154 )
183155
184156
@@ -196,21 +168,28 @@ def __init__(
196168 metrics_backend : MetricsBackend ,
197169 propagate_traces : bool ,
198170 usecase : Usecase ,
199- scope : str ,
171+ scope : Scope ,
172+ token_generator : TokenGenerator | None ,
200173 ):
201174 self ._pool = pool
202175 self ._base_path = base_path
203176 self ._metrics_backend = metrics_backend
204177 self ._propagate_traces = propagate_traces
205178 self ._usecase = usecase
206179 self ._scope = scope
180+ self ._token_generator = token_generator
207181
208182 def _make_headers (self ) -> dict [str , str ]:
209183 headers = dict (self ._pool .headers )
210184 if self ._propagate_traces :
211185 headers .update (
212186 dict (sentry_sdk .get_current_scope ().iter_trace_propagation_headers ())
213187 )
188+ if self ._token_generator :
189+ token = self ._token_generator .sign_for_scope (
190+ self ._usecase .name , self ._scope
191+ )
192+ headers ["Authorization" ] = f"Bearer { token } "
214193 return headers
215194
216195 def _make_url (self , key : str | None , full : bool = False ) -> str :
0 commit comments