Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/google/auth/transport/grpc.py: 37%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

78 statements  

1# Copyright 2016 Google LLC 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14 

15"""Authorization support for gRPC.""" 

16 

17from __future__ import absolute_import 

18 

19import logging 

20import warnings 

21 

22from google.auth import exceptions 

23from google.auth.transport import _mtls_helper 

24from google.auth.transport import mtls 

25from google.oauth2 import service_account 

26 

27try: 

28 import grpc # type: ignore 

29except ImportError as caught_exc: # pragma: NO COVER 

30 raise ImportError( 

31 "gRPC is not installed from please install the grpcio package to use the gRPC transport." 

32 ) from caught_exc 

33 

34 

35_grpc_ver_str = getattr(grpc, "__version__", None) 

36if isinstance(_grpc_ver_str, str): 

37 _parts = [] 

38 for _part in _grpc_ver_str.split("."): 

39 try: 

40 _parts.append(int(_part)) 

41 except ValueError: 

42 break 

43 if _parts and tuple(_parts) < (1, 83, 0): 

44 warnings.warn( 

45 "grpcio < 1.83.0 does not support Post-Quantum Cryptography (PQC). " 

46 "Support for non-PQC environments is deprecated. In October 2026, " 

47 "google-auth will raise its minimum requirements " 

48 "to enforce grpcio >= 1.83.0. " 

49 "For more details on Google Cloud's post-quantum security migration, visit: " 

50 "https://cloud.google.com/security/resources/post-quantum-cryptography", 

51 FutureWarning, 

52 ) 

53 

54_LOGGER = logging.getLogger(__name__) 

55 

56 

57class AuthMetadataPlugin(grpc.AuthMetadataPlugin): 

58 """A `gRPC AuthMetadataPlugin`_ that inserts the credentials into each 

59 request. 

60 

61 .. _gRPC AuthMetadataPlugin: 

62 http://www.grpc.io/grpc/python/grpc.html#grpc.AuthMetadataPlugin 

63 

64 Args: 

65 credentials (google.auth.credentials.Credentials): The credentials to 

66 add to requests. 

67 request (google.auth.transport.Request): A HTTP transport request 

68 object used to refresh credentials as needed. 

69 default_host (Optional[str]): A host like "pubsub.googleapis.com". 

70 This is used when a self-signed JWT is created from service 

71 account credentials. 

72 suppress_metrics_header (bool): When enabled, ``x-goog-api-client`` 

73 will be stripped from authorization headers. 

74 """ 

75 

76 def __init__( 

77 self, credentials, request, default_host=None, *, suppress_metrics_header=False 

78 ): 

79 # pylint: disable=no-value-for-parameter 

80 # pylint doesn't realize that the super method takes no arguments 

81 # because this class is the same name as the superclass. 

82 super(AuthMetadataPlugin, self).__init__() 

83 self._credentials = credentials 

84 self._request = request 

85 self._default_host = default_host 

86 self._suppress_metrics_header = suppress_metrics_header 

87 

88 def _get_authorization_headers(self, context): 

89 """Gets the authorization headers for a request. 

90 

91 Returns: 

92 Sequence[Tuple[str, str]]: A list of request headers (key, value) 

93 to add to the request. 

94 """ 

95 headers = {} 

96 

97 # https://google.aip.dev/auth/4111 

98 # Attempt to use self-signed JWTs when a service account is used. 

99 # A default host must be explicitly provided since it cannot always 

100 # be determined from the context.service_url. 

101 if isinstance(self._credentials, service_account.Credentials): 

102 self._credentials._create_self_signed_jwt( 

103 "https://{}/".format(self._default_host) if self._default_host else None 

104 ) 

105 

106 self._credentials.before_request( 

107 self._request, context.method_name, context.service_url, headers 

108 ) 

109 

110 if self._suppress_metrics_header and "x-goog-api-client" in headers: 

111 del headers["x-goog-api-client"] 

112 

113 return list(headers.items()) 

114 

115 def __call__(self, context, callback): 

116 """Passes authorization metadata into the given callback. 

117 

118 Args: 

119 context (grpc.AuthMetadataContext): The RPC context. 

120 callback (grpc.AuthMetadataPluginCallback): The callback that will 

121 be invoked to pass in the authorization metadata. 

122 """ 

123 callback(self._get_authorization_headers(context), None) 

124 

125 

126def secure_authorized_channel( 

127 credentials, 

128 request, 

129 target, 

130 ssl_credentials=None, 

131 client_cert_callback=None, 

132 **kwargs 

133): 

134 """Creates a secure authorized gRPC channel. 

135 

136 This creates a channel with SSL and :class:`AuthMetadataPlugin`. This 

137 channel can be used to create a stub that can make authorized requests. 

138 Users can configure client certificate or rely on device certificates to 

139 establish a mutual TLS channel, if the `GOOGLE_API_USE_CLIENT_CERTIFICATE` 

140 variable is explicitly set to `true`. 

141 

142 Example:: 

143 

144 import google.auth 

145 import google.auth.transport.grpc 

146 import google.auth.transport.requests 

147 from google.cloud.speech.v1 import cloud_speech_pb2 

148 

149 # Get credentials. 

150 credentials, _ = google.auth.default() 

151 

152 # Get an HTTP request function to refresh credentials. 

153 request = google.auth.transport.requests.Request() 

154 

155 # Create a channel. 

156 channel = google.auth.transport.grpc.secure_authorized_channel( 

157 credentials, regular_endpoint, request, 

158 ssl_credentials=grpc.ssl_channel_credentials()) 

159 

160 # Use the channel to create a stub. 

161 cloud_speech.create_Speech_stub(channel) 

162 

163 Usage: 

164 

165 There are actually a couple of options to create a channel, depending on if 

166 you want to create a regular or mutual TLS channel. 

167 

168 First let's list the endpoints (regular vs mutual TLS) to choose from:: 

169 

170 regular_endpoint = 'speech.googleapis.com:443' 

171 mtls_endpoint = 'speech.mtls.googleapis.com:443' 

172 

173 Option 1: create a regular (non-mutual) TLS channel by explicitly setting 

174 the ssl_credentials:: 

175 

176 regular_ssl_credentials = grpc.ssl_channel_credentials() 

177 

178 channel = google.auth.transport.grpc.secure_authorized_channel( 

179 credentials, request, regular_endpoint, 

180 ssl_credentials=regular_ssl_credentials) 

181 

182 Option 2: create a mutual TLS channel by calling a callback which returns 

183 the client side certificate and the key (Note that 

184 `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be explicitly 

185 set to `true`):: 

186 

187 def my_client_cert_callback(): 

188 code_to_load_client_cert_and_key() 

189 if loaded: 

190 return (pem_cert_bytes, pem_key_bytes) 

191 raise MyClientCertFailureException() 

192 

193 try: 

194 channel = google.auth.transport.grpc.secure_authorized_channel( 

195 credentials, request, mtls_endpoint, 

196 client_cert_callback=my_client_cert_callback) 

197 except MyClientCertFailureException: 

198 # handle the exception 

199 

200 Option 3: use application default SSL credentials. It searches and uses 

201 the command in a context aware metadata file, which is available on devices 

202 with endpoint verification support (Note that 

203 `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be explicitly 

204 set to `true`). 

205 See https://cloud.google.com/endpoint-verification/docs/overview:: 

206 

207 try: 

208 default_ssl_credentials = SslCredentials() 

209 except: 

210 # Exception can be raised if the context aware metadata is malformed. 

211 # See :class:`SslCredentials` for the possible exceptions. 

212 

213 # Choose the endpoint based on the SSL credentials type. 

214 if default_ssl_credentials.is_mtls: 

215 endpoint_to_use = mtls_endpoint 

216 else: 

217 endpoint_to_use = regular_endpoint 

218 channel = google.auth.transport.grpc.secure_authorized_channel( 

219 credentials, request, endpoint_to_use, 

220 ssl_credentials=default_ssl_credentials) 

221 

222 Option 4: not setting ssl_credentials and client_cert_callback. For devices 

223 without endpoint verification support or `GOOGLE_API_USE_CLIENT_CERTIFICATE` 

224 environment variable is not `true`, a regular TLS channel is created; 

225 otherwise, a mutual TLS channel is created, however, the call should be 

226 wrapped in a try/except block in case of malformed context aware metadata. 

227 

228 The following code uses regular_endpoint, it works the same no matter the 

229 created channle is regular or mutual TLS. Regular endpoint ignores client 

230 certificate and key:: 

231 

232 channel = google.auth.transport.grpc.secure_authorized_channel( 

233 credentials, request, regular_endpoint) 

234 

235 The following code uses mtls_endpoint, if the created channle is regular, 

236 and API mtls_endpoint is confgured to require client SSL credentials, API 

237 calls using this channel will be rejected:: 

238 

239 channel = google.auth.transport.grpc.secure_authorized_channel( 

240 credentials, request, mtls_endpoint) 

241 

242 Args: 

243 credentials (google.auth.credentials.Credentials): The credentials to 

244 add to requests. 

245 request (google.auth.transport.Request): A HTTP transport request 

246 object used to refresh credentials as needed. Even though gRPC 

247 is a separate transport, there's no way to refresh the credentials 

248 without using a standard http transport. 

249 target (str): The host and port of the service. 

250 ssl_credentials (grpc.ChannelCredentials): Optional SSL channel 

251 credentials. This can be used to specify different certificates. 

252 This argument is mutually exclusive with client_cert_callback; 

253 providing both will raise an exception. 

254 If ssl_credentials and client_cert_callback are None, application 

255 default SSL credentials are used if `GOOGLE_API_USE_CLIENT_CERTIFICATE` 

256 environment variable is explicitly set to `true`, otherwise one way TLS 

257 SSL credentials are used. 

258 client_cert_callback (Callable[[], (bytes, bytes)]): Optional 

259 callback function to obtain client certicate and key for mutual TLS 

260 connection. This argument is mutually exclusive with 

261 ssl_credentials; providing both will raise an exception. 

262 This argument does nothing unless `GOOGLE_API_USE_CLIENT_CERTIFICATE` 

263 environment variable is explicitly set to `true`. 

264 kwargs: Additional arguments to pass to :func:`grpc.secure_channel`. 

265 

266 Returns: 

267 grpc.Channel: The created gRPC channel. 

268 

269 Raises: 

270 google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel 

271 creation failed for any reason. 

272 """ 

273 # Create the metadata plugin for inserting the authorization header. 

274 metadata_plugin = AuthMetadataPlugin(credentials, request) 

275 

276 # Create a set of grpc.CallCredentials using the metadata plugin. 

277 google_auth_credentials = grpc.metadata_call_credentials(metadata_plugin) 

278 

279 if ssl_credentials and client_cert_callback: 

280 raise exceptions.MalformedError( 

281 "Received both ssl_credentials and client_cert_callback; " 

282 "these are mutually exclusive." 

283 ) 

284 

285 # If SSL credentials are not explicitly set, try client_cert_callback and ADC. 

286 if not ssl_credentials: 

287 use_client_cert = _mtls_helper.check_use_client_cert() 

288 if use_client_cert and client_cert_callback: 

289 # Use the callback if provided. 

290 cert, key = client_cert_callback() 

291 ssl_credentials = grpc.ssl_channel_credentials( 

292 certificate_chain=cert, private_key=key 

293 ) 

294 elif use_client_cert: 

295 # Use application default SSL credentials. 

296 adc_ssl_credentils = SslCredentials() 

297 ssl_credentials = adc_ssl_credentils.ssl_credentials 

298 else: 

299 ssl_credentials = grpc.ssl_channel_credentials() 

300 

301 # Combine the ssl credentials and the authorization credentials. 

302 composite_credentials = grpc.composite_channel_credentials( 

303 ssl_credentials, google_auth_credentials 

304 ) 

305 

306 return grpc.secure_channel(target, composite_credentials, **kwargs) 

307 

308 

309class SslCredentials: 

310 """Class for application default SSL credentials. 

311 

312 Mutual TLS (mTLS) is enabled if either: 

313 

314 1. The `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is explicitly 

315 set to `"true"`. 

316 2. The `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is unset or empty, 

317 but a valid workload certificate configuration is found (e.g., via the 

318 `GOOGLE_API_CERTIFICATE_CONFIG` environment variable or the default gcloud config path). 

319 

320 See https://google.aip.dev/auth/4114 for client certificate discovery details. 

321 

322 If client certificate usage is enabled, then for devices with endpoint 

323 verification support, a device certificate will be automatically loaded and 

324 mutual TLS will be established. 

325 See https://cloud.google.com/endpoint-verification/docs/overview. 

326 """ 

327 

328 def __init__(self): 

329 use_client_cert = _mtls_helper.check_use_client_cert() 

330 if not use_client_cert: 

331 self._is_mtls = False 

332 else: 

333 self._is_mtls = mtls.has_default_client_cert_source() 

334 

335 @property 

336 def ssl_credentials(self): 

337 """Get the created SSL channel credentials. 

338 

339 For devices with endpoint verification support, if the device certificate 

340 loading has any problems, corresponding exceptions will be raised. For 

341 a device without endpoint verification support, no exceptions will be 

342 raised. 

343 

344 Returns: 

345 grpc.ChannelCredentials: The created grpc channel credentials. 

346 

347 Raises: 

348 google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel 

349 creation failed for any reason. 

350 """ 

351 if self._is_mtls: 

352 try: 

353 has_cert, cert, key, _ = _mtls_helper.get_client_ssl_credentials() 

354 if has_cert: 

355 self._ssl_credentials = grpc.ssl_channel_credentials( 

356 certificate_chain=cert, private_key=key 

357 ) 

358 else: 

359 self._ssl_credentials = grpc.ssl_channel_credentials() 

360 self._is_mtls = False 

361 except (exceptions.ClientCertError, OSError) as caught_exc: 

362 new_exc = exceptions.MutualTLSChannelError(caught_exc) 

363 raise new_exc from caught_exc 

364 else: 

365 self._ssl_credentials = grpc.ssl_channel_credentials() 

366 

367 return self._ssl_credentials 

368 

369 @property 

370 def is_mtls(self): 

371 """Indicates if the created SSL channel credentials is mutual TLS.""" 

372 return self._is_mtls