33
44from __future__ import annotations
55
6- from typing import Any , AsyncIterable , Callable , Iterable , TypeVar , cast
76from urllib .parse import urlparse
87
9- from grpc import (
10- AuthMetadataContext ,
11- AuthMetadataPlugin ,
12- AuthMetadataPluginCallback ,
13- ChannelCredentials ,
14- composite_channel_credentials ,
15- metadata_call_credentials ,
16- ssl_channel_credentials ,
17- )
18- from grpc .aio import (
19- Channel ,
20- ClientCallDetails ,
21- ClientInterceptor ,
22- StreamStreamCall ,
23- StreamStreamClientInterceptor ,
24- StreamUnaryCall ,
25- StreamUnaryClientInterceptor ,
26- UnaryStreamCall ,
27- UnaryStreamClientInterceptor ,
28- UnaryUnaryCall ,
29- UnaryUnaryClientInterceptor ,
30- insecure_channel ,
31- secure_channel ,
32- )
8+ from grpc import ssl_channel_credentials
9+ from grpc .aio import Channel , insecure_channel , secure_channel
3310
34- from ..auth import get_token
3511from ..config import Config
3612
3713__all__ = ["create_channel" ]
3814
39- RequestType = TypeVar ("RequestType" )
40- RequestIterableType = Iterable [Any ] | AsyncIterable [Any ]
41- ResponseIterableType = AsyncIterable [Any ]
42-
4315
4416def create_channel (config : Config ) -> Channel :
4517 """
4618 Create a :class:`Channel` for the specified configuration.
19+
20+ Note that the returned channel never carries authorization; the caller is expected to supply
21+ auth tokens on every request.
4722 """
4823 u = urlparse (config .url .url )
4924
@@ -64,125 +39,6 @@ def create_channel(config: Config) -> Channel:
6439 certificate_chain = config .ssl .cert ,
6540 )
6641
67- # The grpc Credential objects do not actually define a formal interface, and are
68- # used interchangeably in the code.
69- #
70- # Additionally there are some incorrect rules in the grpc-stubs typing rules that force
71- # us to work around the type system.
72- credentials = cast (
73- ChannelCredentials ,
74- composite_channel_credentials (
75- credentials , metadata_call_credentials (GrpcAuth (config ), name = "auth gateway" )
76- ),
77- )
7842 return secure_channel (u .netloc , credentials , tuple (options ))
79-
8043 else :
81- # Python/C++ libraries refuse to allow "credentials" objects to be passed around on
82- # non-TLS channels, but they don't check interceptors; use an interceptor to inject
83- # an Authorization header instead
84- #
85- # There must be a distinct interceptor instance for each of
86- # the four Request/Response arities. There is a known issue in
87- # the underlying gRPC code that prevents reuse of a single
88- # multiply-inherited interceptor.
89- #
90- # https://github.com/grpc/grpc/issues/31442
91- return insecure_channel (
92- u .netloc ,
93- options ,
94- interceptors = [
95- cast (ClientInterceptor , GrpcAuthUnaryUnaryClientInterceptor (config )),
96- cast (ClientInterceptor , GrpcAuthUnaryStreamClientInterceptor (config )),
97- cast (ClientInterceptor , GrpcAuthStreamUnaryClientInterceptor (config )),
98- cast (ClientInterceptor , GrpcAuthStreamStreamClientInterceptor (config )),
99- ],
100- ) # type: ignore
101-
102-
103- class GrpcAuth (AuthMetadataPlugin ):
104- def __init__ (self , config : Config ):
105- self ._config = config
106-
107- def __call__ (self , context : AuthMetadataContext , callback : AuthMetadataPluginCallback ):
108- # This overly verbose type signature is here to satisfy mypy and grpc-stubs
109- options = list [tuple [str , str | bytes ]]()
110-
111- token = get_token (self ._config .access .token )
112- if token :
113- # note: gRPC headers MUST be lowercased
114- options .append (("authorization" , "Bearer " + token ))
115-
116- callback (tuple (options ), None )
117-
118-
119- class GrpcAuthInterceptor :
120- """
121- An interceptor that injects "Authorization" metadata into a request.
122-
123- This works around the fact that the C++ gRPC libraries (which Python is built on) highly
124- discourage sending authorization data over the wire unless the connection is protected with TLS.
125- """
126-
127- # NOTE: There are a number of typing errors in the grpc.aio classes, so we're ignoring a handful
128- # of lines until those problems are addressed.
129-
130- def __init__ (self , config : Config ):
131- self ._config = config
132-
133- def _modify_client_call_details (self , client_call_details : ClientCallDetails ):
134- if (
135- client_call_details .metadata is not None
136- and "authorization" not in client_call_details .metadata
137- ):
138- token = get_token (self ._config .access .token )
139- if token is not None :
140- client_call_details .metadata .add ("authorization" , f"Bearer { token } " )
141-
142- return client_call_details
143-
144-
145- class GrpcAuthUnaryUnaryClientInterceptor (GrpcAuthInterceptor , UnaryUnaryClientInterceptor ):
146- async def intercept_unary_unary (
147- self ,
148- continuation : Callable [[ClientCallDetails , RequestType ], UnaryUnaryCall ],
149- client_call_details : ClientCallDetails ,
150- request : RequestType ,
151- ) -> UnaryUnaryCall | RequestType :
152- return await continuation (self ._modify_client_call_details (client_call_details ), request )
153-
154-
155- class GrpcAuthUnaryStreamClientInterceptor (GrpcAuthInterceptor , UnaryStreamClientInterceptor ):
156- async def intercept_unary_stream (
157- self ,
158- continuation : Callable [[ClientCallDetails , RequestType ], UnaryStreamCall ],
159- client_call_details : ClientCallDetails ,
160- request : RequestType ,
161- ) -> ResponseIterableType | UnaryStreamCall :
162- return await continuation ( # type: ignore
163- self ._modify_client_call_details (client_call_details ), request
164- )
165-
166-
167- class GrpcAuthStreamUnaryClientInterceptor (GrpcAuthInterceptor , StreamUnaryClientInterceptor ):
168- async def intercept_stream_unary ( # type: ignore
169- self ,
170- continuation : Callable [[ClientCallDetails , RequestType ], StreamUnaryCall ],
171- client_call_details : ClientCallDetails ,
172- request_iterator : RequestIterableType ,
173- ) -> StreamUnaryCall :
174- return await continuation (
175- self ._modify_client_call_details (client_call_details ), request_iterator # type: ignore
176- )
177-
178-
179- class GrpcAuthStreamStreamClientInterceptor (GrpcAuthInterceptor , StreamStreamClientInterceptor ):
180- async def intercept_stream_stream (
181- self ,
182- continuation : Callable [[ClientCallDetails , RequestType ], StreamStreamCall ],
183- client_call_details : ClientCallDetails ,
184- request_iterator : RequestIterableType ,
185- ) -> ResponseIterableType | StreamStreamCall :
186- return await continuation ( # type: ignore
187- self ._modify_client_call_details (client_call_details ), request_iterator # type: ignore
188- )
44+ return insecure_channel (u .netloc , options )
0 commit comments