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

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

64 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 """ 

52 

53 def __init__(self, credentials, request, default_host=None): 

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

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

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

57 super(AuthMetadataPlugin, self).__init__() 

58 self._credentials = credentials 

59 self._request = request 

60 self._default_host = default_host 

61 

62 def _get_authorization_headers(self, context): 

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

64 

65 Returns: 

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

67 to add to the request. 

68 """ 

69 headers = {} 

70 

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

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

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

74 # be determined from the context.service_url. 

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

76 self._credentials._create_self_signed_jwt( 

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

78 ) 

79 

80 self._credentials.before_request( 

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

82 ) 

83 

84 return list(headers.items()) 

85 

86 def __call__(self, context, callback): 

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

88 

89 Args: 

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

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

92 be invoked to pass in the authorization metadata. 

93 """ 

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

95 

96 

97def secure_authorized_channel( 

98 credentials, 

99 request, 

100 target, 

101 ssl_credentials=None, 

102 client_cert_callback=None, 

103 **kwargs 

104): 

105 """Creates a secure authorized gRPC channel. 

106 

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

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

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

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

111 variable is explicitly set to `true`. 

112 

113 Example:: 

114 

115 import google.auth 

116 import google.auth.transport.grpc 

117 import google.auth.transport.requests 

118 from google.cloud.speech.v1 import cloud_speech_pb2 

119 

120 # Get credentials. 

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

122 

123 # Get an HTTP request function to refresh credentials. 

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

125 

126 # Create a channel. 

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

128 credentials, regular_endpoint, request, 

129 ssl_credentials=grpc.ssl_channel_credentials()) 

130 

131 # Use the channel to create a stub. 

132 cloud_speech.create_Speech_stub(channel) 

133 

134 Usage: 

135 

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

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

138 

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

140 

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

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

143 

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

145 the ssl_credentials:: 

146 

147 regular_ssl_credentials = grpc.ssl_channel_credentials() 

148 

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

150 credentials, request, regular_endpoint, 

151 ssl_credentials=regular_ssl_credentials) 

152 

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

154 the client side certificate and the key (Note that 

155 `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be explicitly 

156 set to `true`):: 

157 

158 def my_client_cert_callback(): 

159 code_to_load_client_cert_and_key() 

160 if loaded: 

161 return (pem_cert_bytes, pem_key_bytes) 

162 raise MyClientCertFailureException() 

163 

164 try: 

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

166 credentials, request, mtls_endpoint, 

167 client_cert_callback=my_client_cert_callback) 

168 except MyClientCertFailureException: 

169 # handle the exception 

170 

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

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

173 with endpoint verification support (Note that 

174 `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be explicitly 

175 set to `true`). 

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

177 

178 try: 

179 default_ssl_credentials = SslCredentials() 

180 except: 

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

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

183 

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

185 if default_ssl_credentials.is_mtls: 

186 endpoint_to_use = mtls_endpoint 

187 else: 

188 endpoint_to_use = regular_endpoint 

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

190 credentials, request, endpoint_to_use, 

191 ssl_credentials=default_ssl_credentials) 

192 

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

194 without endpoint verification support or `GOOGLE_API_USE_CLIENT_CERTIFICATE` 

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

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

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

198 

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

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

201 certificate and key:: 

202 

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

204 credentials, request, regular_endpoint) 

205 

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

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

208 calls using this channel will be rejected:: 

209 

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

211 credentials, request, mtls_endpoint) 

212 

213 Args: 

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

215 add to requests. 

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

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

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

219 without using a standard http transport. 

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

221 ssl_credentials (grpc.ChannelCredentials): Optional SSL channel 

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

223 This argument is mutually exclusive with client_cert_callback; 

224 providing both will raise an exception. 

225 If ssl_credentials and client_cert_callback are None, application 

226 default SSL credentials are used if `GOOGLE_API_USE_CLIENT_CERTIFICATE` 

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

228 SSL credentials are used. 

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

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

231 connection. This argument is mutually exclusive with 

232 ssl_credentials; providing both will raise an exception. 

233 This argument does nothing unless `GOOGLE_API_USE_CLIENT_CERTIFICATE` 

234 environment variable is explicitly set to `true`. 

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

236 

237 Returns: 

238 grpc.Channel: The created gRPC channel. 

239 

240 Raises: 

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

242 creation failed for any reason. 

243 """ 

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

245 metadata_plugin = AuthMetadataPlugin(credentials, request) 

246 

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

248 google_auth_credentials = grpc.metadata_call_credentials(metadata_plugin) 

249 

250 if ssl_credentials and client_cert_callback: 

251 raise exceptions.MalformedError( 

252 "Received both ssl_credentials and client_cert_callback; " 

253 "these are mutually exclusive." 

254 ) 

255 

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

257 if not ssl_credentials: 

258 use_client_cert = _mtls_helper.check_use_client_cert() 

259 if use_client_cert and client_cert_callback: 

260 # Use the callback if provided. 

261 cert, key = client_cert_callback() 

262 ssl_credentials = grpc.ssl_channel_credentials( 

263 certificate_chain=cert, private_key=key 

264 ) 

265 elif use_client_cert: 

266 # Use application default SSL credentials. 

267 adc_ssl_credentils = SslCredentials() 

268 ssl_credentials = adc_ssl_credentils.ssl_credentials 

269 else: 

270 ssl_credentials = grpc.ssl_channel_credentials() 

271 

272 # Combine the ssl credentials and the authorization credentials. 

273 composite_credentials = grpc.composite_channel_credentials( 

274 ssl_credentials, google_auth_credentials 

275 ) 

276 

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

278 

279 

280class SslCredentials: 

281 """Class for application default SSL credentials. 

282 

283 Mutual TLS (mTLS) is enabled if either: 

284 

285 1. The `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is explicitly 

286 set to `"true"`. 

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

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

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

290 

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

292 

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

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

295 mutual TLS will be established. 

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

297 """ 

298 

299 def __init__(self): 

300 use_client_cert = _mtls_helper.check_use_client_cert() 

301 if not use_client_cert: 

302 self._is_mtls = False 

303 else: 

304 self._is_mtls = mtls.has_default_client_cert_source() 

305 

306 @property 

307 def ssl_credentials(self): 

308 """Get the created SSL channel credentials. 

309 

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

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

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

313 raised. 

314 

315 Returns: 

316 grpc.ChannelCredentials: The created grpc channel credentials. 

317 

318 Raises: 

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

320 creation failed for any reason. 

321 """ 

322 if self._is_mtls: 

323 try: 

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

325 if has_cert: 

326 self._ssl_credentials = grpc.ssl_channel_credentials( 

327 certificate_chain=cert, private_key=key 

328 ) 

329 else: 

330 self._ssl_credentials = grpc.ssl_channel_credentials() 

331 self._is_mtls = False 

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

333 new_exc = exceptions.MutualTLSChannelError(caught_exc) 

334 raise new_exc from caught_exc 

335 else: 

336 self._ssl_credentials = grpc.ssl_channel_credentials() 

337 

338 return self._ssl_credentials 

339 

340 @property 

341 def is_mtls(self): 

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

343 return self._is_mtls