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

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

67 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 

20 

21from google.auth import exceptions 

22from google.auth.transport import _mtls_helper 

23from google.auth.transport import 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_LOGGER = logging.getLogger(__name__) 

34 

35 

36class AuthMetadataPlugin(grpc.AuthMetadataPlugin): 

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

38 request. 

39 

40 .. _gRPC AuthMetadataPlugin: 

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

42 

43 Args: 

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

45 add to requests. 

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

47 object used to refresh credentials as needed. 

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

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

50 account credentials. 

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

52 will be stripped from authorization headers. 

53 """ 

54 

55 def __init__( 

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

57 ): 

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

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

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

61 super(AuthMetadataPlugin, self).__init__() 

62 self._credentials = credentials 

63 self._request = request 

64 self._default_host = default_host 

65 self._suppress_metrics_header = suppress_metrics_header 

66 

67 def _get_authorization_headers(self, context): 

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

69 

70 Returns: 

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

72 to add to the request. 

73 """ 

74 headers = {} 

75 

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

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

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

79 # be determined from the context.service_url. 

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

81 self._credentials._create_self_signed_jwt( 

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

83 ) 

84 

85 self._credentials.before_request( 

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

87 ) 

88 

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

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

91 

92 return list(headers.items()) 

93 

94 def __call__(self, context, callback): 

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

96 

97 Args: 

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

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

100 be invoked to pass in the authorization metadata. 

101 """ 

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

103 

104 

105def secure_authorized_channel( 

106 credentials, 

107 request, 

108 target, 

109 ssl_credentials=None, 

110 client_cert_callback=None, 

111 **kwargs 

112): 

113 """Creates a secure authorized gRPC channel. 

114 

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

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

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

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

119 variable is explicitly set to `true`. 

120 

121 Example:: 

122 

123 import google.auth 

124 import google.auth.transport.grpc 

125 import google.auth.transport.requests 

126 from google.cloud.speech.v1 import cloud_speech_pb2 

127 

128 # Get credentials. 

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

130 

131 # Get an HTTP request function to refresh credentials. 

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

133 

134 # Create a channel. 

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

136 credentials, regular_endpoint, request, 

137 ssl_credentials=grpc.ssl_channel_credentials()) 

138 

139 # Use the channel to create a stub. 

140 cloud_speech.create_Speech_stub(channel) 

141 

142 Usage: 

143 

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

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

146 

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

148 

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

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

151 

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

153 the ssl_credentials:: 

154 

155 regular_ssl_credentials = grpc.ssl_channel_credentials() 

156 

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

158 credentials, request, regular_endpoint, 

159 ssl_credentials=regular_ssl_credentials) 

160 

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

162 the client side certificate and the key (Note that 

163 `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be explicitly 

164 set to `true`):: 

165 

166 def my_client_cert_callback(): 

167 code_to_load_client_cert_and_key() 

168 if loaded: 

169 return (pem_cert_bytes, pem_key_bytes) 

170 raise MyClientCertFailureException() 

171 

172 try: 

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

174 credentials, request, mtls_endpoint, 

175 client_cert_callback=my_client_cert_callback) 

176 except MyClientCertFailureException: 

177 # handle the exception 

178 

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

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

181 with endpoint verification support (Note that 

182 `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be explicitly 

183 set to `true`). 

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

185 

186 try: 

187 default_ssl_credentials = SslCredentials() 

188 except: 

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

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

191 

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

193 if default_ssl_credentials.is_mtls: 

194 endpoint_to_use = mtls_endpoint 

195 else: 

196 endpoint_to_use = regular_endpoint 

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

198 credentials, request, endpoint_to_use, 

199 ssl_credentials=default_ssl_credentials) 

200 

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

202 without endpoint verification support or `GOOGLE_API_USE_CLIENT_CERTIFICATE` 

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

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

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

206 

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

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

209 certificate and key:: 

210 

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

212 credentials, request, regular_endpoint) 

213 

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

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

216 calls using this channel will be rejected:: 

217 

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

219 credentials, request, mtls_endpoint) 

220 

221 Args: 

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

223 add to requests. 

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

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

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

227 without using a standard http transport. 

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

229 ssl_credentials (grpc.ChannelCredentials): Optional SSL channel 

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

231 This argument is mutually exclusive with client_cert_callback; 

232 providing both will raise an exception. 

233 If ssl_credentials and client_cert_callback are None, application 

234 default SSL credentials are used if `GOOGLE_API_USE_CLIENT_CERTIFICATE` 

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

236 SSL credentials are used. 

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

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

239 connection. This argument is mutually exclusive with 

240 ssl_credentials; providing both will raise an exception. 

241 This argument does nothing unless `GOOGLE_API_USE_CLIENT_CERTIFICATE` 

242 environment variable is explicitly set to `true`. 

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

244 

245 Returns: 

246 grpc.Channel: The created gRPC channel. 

247 

248 Raises: 

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

250 creation failed for any reason. 

251 """ 

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

253 metadata_plugin = AuthMetadataPlugin(credentials, request) 

254 

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

256 google_auth_credentials = grpc.metadata_call_credentials(metadata_plugin) 

257 

258 if ssl_credentials and client_cert_callback: 

259 raise exceptions.MalformedError( 

260 "Received both ssl_credentials and client_cert_callback; " 

261 "these are mutually exclusive." 

262 ) 

263 

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

265 if not ssl_credentials: 

266 use_client_cert = _mtls_helper.check_use_client_cert() 

267 if use_client_cert and client_cert_callback: 

268 # Use the callback if provided. 

269 cert, key = client_cert_callback() 

270 ssl_credentials = grpc.ssl_channel_credentials( 

271 certificate_chain=cert, private_key=key 

272 ) 

273 elif use_client_cert: 

274 # Use application default SSL credentials. 

275 adc_ssl_credentils = SslCredentials() 

276 ssl_credentials = adc_ssl_credentils.ssl_credentials 

277 else: 

278 ssl_credentials = grpc.ssl_channel_credentials() 

279 

280 # Combine the ssl credentials and the authorization credentials. 

281 composite_credentials = grpc.composite_channel_credentials( 

282 ssl_credentials, google_auth_credentials 

283 ) 

284 

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

286 

287 

288class SslCredentials: 

289 """Class for application default SSL credentials. 

290 

291 Mutual TLS (mTLS) is enabled if either: 

292 

293 1. The `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is explicitly 

294 set to `"true"`. 

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

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

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

298 

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

300 

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

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

303 mutual TLS will be established. 

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

305 """ 

306 

307 def __init__(self): 

308 use_client_cert = _mtls_helper.check_use_client_cert() 

309 if not use_client_cert: 

310 self._is_mtls = False 

311 else: 

312 self._is_mtls = mtls.has_default_client_cert_source() 

313 

314 @property 

315 def ssl_credentials(self): 

316 """Get the created SSL channel credentials. 

317 

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

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

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

321 raised. 

322 

323 Returns: 

324 grpc.ChannelCredentials: The created grpc channel credentials. 

325 

326 Raises: 

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

328 creation failed for any reason. 

329 """ 

330 if self._is_mtls: 

331 try: 

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

333 if has_cert: 

334 self._ssl_credentials = grpc.ssl_channel_credentials( 

335 certificate_chain=cert, private_key=key 

336 ) 

337 else: 

338 self._ssl_credentials = grpc.ssl_channel_credentials() 

339 self._is_mtls = False 

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

341 new_exc = exceptions.MutualTLSChannelError(caught_exc) 

342 raise new_exc from caught_exc 

343 else: 

344 self._ssl_credentials = grpc.ssl_channel_credentials() 

345 

346 return self._ssl_credentials 

347 

348 @property 

349 def is_mtls(self): 

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

351 return self._is_mtls