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

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

77 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, mtls 

24from google.oauth2 import service_account 

25 

26try: 

27 import grpc # type: ignore 

28except ImportError as caught_exc: # pragma: NO COVER 

29 raise ImportError( 

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

31 ) from caught_exc 

32 

33 

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

35if isinstance(_grpc_ver_str, str): 

36 _parts = [] 

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

38 try: 

39 _parts.append(int(_part)) 

40 except ValueError: 

41 break 

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

43 warnings.warn( 

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

45 "Support for non-PQC environments is deprecated. In April 2027, " 

46 "google-auth will raise its minimum requirements " 

47 "to enforce grpcio >= 1.83.0. " 

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

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

50 FutureWarning, 

51 ) 

52 

53_LOGGER = logging.getLogger(__name__) 

54 

55 

56class AuthMetadataPlugin(grpc.AuthMetadataPlugin): 

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

58 request. 

59 

60 .. _gRPC AuthMetadataPlugin: 

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

62 

63 Args: 

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

65 add to requests. 

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

67 object used to refresh credentials as needed. 

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

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

70 account credentials. 

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

72 will be stripped from authorization headers. 

73 """ 

74 

75 def __init__( 

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

77 ): 

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

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

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

81 super(AuthMetadataPlugin, self).__init__() 

82 self._credentials = credentials 

83 self._request = request 

84 self._default_host = default_host 

85 self._suppress_metrics_header = suppress_metrics_header 

86 

87 def _get_authorization_headers(self, context): 

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

89 

90 Returns: 

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

92 to add to the request. 

93 """ 

94 headers = {} 

95 

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

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

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

99 # be determined from the context.service_url. 

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

101 self._credentials._create_self_signed_jwt( 

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

103 ) 

104 

105 self._credentials.before_request( 

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

107 ) 

108 

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

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

111 

112 return list(headers.items()) 

113 

114 def __call__(self, context, callback): 

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

116 

117 Args: 

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

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

120 be invoked to pass in the authorization metadata. 

121 """ 

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

123 

124 

125def secure_authorized_channel( 

126 credentials, 

127 request, 

128 target, 

129 ssl_credentials=None, 

130 client_cert_callback=None, 

131 **kwargs, 

132): 

133 """Creates a secure authorized gRPC channel. 

134 

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

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

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

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

139 variable is explicitly set to `true`. 

140 

141 Example:: 

142 

143 import google.auth 

144 import google.auth.transport.grpc 

145 import google.auth.transport.requests 

146 from google.cloud.speech.v1 import cloud_speech_pb2 

147 

148 # Get credentials. 

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

150 

151 # Get an HTTP request function to refresh credentials. 

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

153 

154 # Create a channel. 

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

156 credentials, regular_endpoint, request, 

157 ssl_credentials=grpc.ssl_channel_credentials()) 

158 

159 # Use the channel to create a stub. 

160 cloud_speech.create_Speech_stub(channel) 

161 

162 Usage: 

163 

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

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

166 

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

168 

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

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

171 

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

173 the ssl_credentials:: 

174 

175 regular_ssl_credentials = grpc.ssl_channel_credentials() 

176 

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

178 credentials, request, regular_endpoint, 

179 ssl_credentials=regular_ssl_credentials) 

180 

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

182 the client side certificate and the key (Note that 

183 `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be explicitly 

184 set to `true`):: 

185 

186 def my_client_cert_callback(): 

187 code_to_load_client_cert_and_key() 

188 if loaded: 

189 return (pem_cert_bytes, pem_key_bytes) 

190 raise MyClientCertFailureException() 

191 

192 try: 

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

194 credentials, request, mtls_endpoint, 

195 client_cert_callback=my_client_cert_callback) 

196 except MyClientCertFailureException: 

197 # handle the exception 

198 

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

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

201 with endpoint verification support (Note that 

202 `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be explicitly 

203 set to `true`). 

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

205 

206 try: 

207 default_ssl_credentials = SslCredentials() 

208 except: 

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

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

211 

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

213 if default_ssl_credentials.is_mtls: 

214 endpoint_to_use = mtls_endpoint 

215 else: 

216 endpoint_to_use = regular_endpoint 

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

218 credentials, request, endpoint_to_use, 

219 ssl_credentials=default_ssl_credentials) 

220 

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

222 without endpoint verification support or `GOOGLE_API_USE_CLIENT_CERTIFICATE` 

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

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

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

226 

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

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

229 certificate and key:: 

230 

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

232 credentials, request, regular_endpoint) 

233 

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

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

236 calls using this channel will be rejected:: 

237 

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

239 credentials, request, mtls_endpoint) 

240 

241 Args: 

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

243 add to requests. 

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

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

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

247 without using a standard http transport. 

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

249 ssl_credentials (grpc.ChannelCredentials): Optional SSL channel 

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

251 This argument is mutually exclusive with client_cert_callback; 

252 providing both will raise an exception. 

253 If ssl_credentials and client_cert_callback are None, application 

254 default SSL credentials are used if `GOOGLE_API_USE_CLIENT_CERTIFICATE` 

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

256 SSL credentials are used. 

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

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

259 connection. This argument is mutually exclusive with 

260 ssl_credentials; providing both will raise an exception. 

261 This argument does nothing unless `GOOGLE_API_USE_CLIENT_CERTIFICATE` 

262 environment variable is explicitly set to `true`. 

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

264 

265 Returns: 

266 grpc.Channel: The created gRPC channel. 

267 

268 Raises: 

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

270 creation failed for any reason. 

271 """ 

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

273 metadata_plugin = AuthMetadataPlugin(credentials, request) 

274 

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

276 google_auth_credentials = grpc.metadata_call_credentials(metadata_plugin) 

277 

278 if ssl_credentials and client_cert_callback: 

279 raise exceptions.MalformedError( 

280 "Received both ssl_credentials and client_cert_callback; " 

281 "these are mutually exclusive." 

282 ) 

283 

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

285 if not ssl_credentials: 

286 use_client_cert = _mtls_helper.check_use_client_cert() 

287 if use_client_cert and client_cert_callback: 

288 # Use the callback if provided. 

289 cert, key = client_cert_callback() 

290 ssl_credentials = grpc.ssl_channel_credentials( 

291 certificate_chain=cert, private_key=key 

292 ) 

293 elif use_client_cert: 

294 # Use application default SSL credentials. 

295 adc_ssl_credentils = SslCredentials() 

296 ssl_credentials = adc_ssl_credentils.ssl_credentials 

297 else: 

298 ssl_credentials = grpc.ssl_channel_credentials() 

299 

300 # Combine the ssl credentials and the authorization credentials. 

301 composite_credentials = grpc.composite_channel_credentials( 

302 ssl_credentials, google_auth_credentials 

303 ) 

304 

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

306 

307 

308class SslCredentials: 

309 """Class for application default SSL credentials. 

310 

311 Mutual TLS (mTLS) is enabled if either: 

312 

313 1. The `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is explicitly 

314 set to `"true"`. 

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

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

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

318 

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

320 

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

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

323 mutual TLS will be established. 

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

325 """ 

326 

327 def __init__(self): 

328 use_client_cert = _mtls_helper.check_use_client_cert() 

329 if not use_client_cert: 

330 self._is_mtls = False 

331 else: 

332 self._is_mtls = mtls.has_default_client_cert_source() 

333 

334 @property 

335 def ssl_credentials(self): 

336 """Get the created SSL channel credentials. 

337 

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

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

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

341 raised. 

342 

343 Returns: 

344 grpc.ChannelCredentials: The created grpc channel credentials. 

345 

346 Raises: 

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

348 creation failed for any reason. 

349 """ 

350 if self._is_mtls: 

351 try: 

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

353 if has_cert: 

354 self._ssl_credentials = grpc.ssl_channel_credentials( 

355 certificate_chain=cert, private_key=key 

356 ) 

357 else: 

358 self._ssl_credentials = grpc.ssl_channel_credentials() 

359 self._is_mtls = False 

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

361 new_exc = exceptions.MutualTLSChannelError(caught_exc) 

362 raise new_exc from caught_exc 

363 else: 

364 self._ssl_credentials = grpc.ssl_channel_credentials() 

365 

366 return self._ssl_credentials 

367 

368 @property 

369 def is_mtls(self): 

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

371 return self._is_mtls