Skip to content

Commit 94c76e0

Browse files
authored
feat: allow default_host and default_scopes to be passed to create_channel (googleapis#134)
Add `default_host` and `default_scopes` parameters to `create_channel` so self-signed JWTs can be used.
1 parent db48ea3 commit 94c76e0

File tree

6 files changed

+429
-26
lines changed

6 files changed

+429
-26
lines changed

google/api_core/grpc_helpers.py

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import collections
1818

1919
import grpc
20+
from packaging import version
21+
import pkg_resources
2022
import six
2123

2224
from google.api_core import exceptions
@@ -33,6 +35,20 @@
3335
except ImportError:
3436
HAS_GRPC_GCP = False
3537

38+
try:
39+
# google.auth.__version__ was added in 1.26.0
40+
_GOOGLE_AUTH_VERSION = google.auth.__version__
41+
except AttributeError:
42+
try: # try pkg_resources if it is available
43+
_GOOGLE_AUTH_VERSION = pkg_resources.get_distribution("google-auth").version
44+
except pkg_resources.DistributionNotFound: # pragma: NO COVER
45+
_GOOGLE_AUTH_VERSION = None
46+
47+
if _GOOGLE_AUTH_VERSION is not None and version.parse(_GOOGLE_AUTH_VERSION) >= version.parse("1.25.0"):
48+
_GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST = True
49+
else:
50+
_GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST = False
51+
3652
# The list of gRPC Callable interfaces that return iterators.
3753
_STREAM_WRAP_CLASSES = (grpc.UnaryStreamMultiCallable, grpc.StreamStreamMultiCallable)
3854

@@ -179,9 +195,11 @@ def wrap_errors(callable_):
179195
def _create_composite_credentials(
180196
credentials=None,
181197
credentials_file=None,
198+
default_scopes=None,
182199
scopes=None,
183200
ssl_credentials=None,
184-
quota_project_id=None):
201+
quota_project_id=None,
202+
default_host=None):
185203
"""Create the composite credentials for secure channels.
186204
187205
Args:
@@ -191,12 +209,16 @@ def _create_composite_credentials(
191209
credentials_file (str): A file with credentials that can be loaded with
192210
:func:`google.auth.load_credentials_from_file`. This argument is
193211
mutually exclusive with credentials.
212+
default_scopes (Sequence[str]): A optional list of scopes needed for this
213+
service. These are only used when credentials are not specified and
214+
are passed to :func:`google.auth.default`.
194215
scopes (Sequence[str]): A optional list of scopes needed for this
195216
service. These are only used when credentials are not specified and
196217
are passed to :func:`google.auth.default`.
197218
ssl_credentials (grpc.ChannelCredentials): Optional SSL channel
198219
credentials. This can be used to specify different certificates.
199220
quota_project_id (str): An optional project to use for billing and quota.
221+
default_host (str): The default endpoint. e.g., "pubsub.googleapis.com".
200222
201223
Returns:
202224
grpc.ChannelCredentials: The composed channel credentials object.
@@ -210,21 +232,55 @@ def _create_composite_credentials(
210232
)
211233

212234
if credentials_file:
213-
credentials, _ = google.auth.load_credentials_from_file(credentials_file, scopes=scopes)
235+
# TODO: remove this if/else once google-auth >= 1.25.0 is required
236+
if _GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
237+
credentials, _ = google.auth.load_credentials_from_file(
238+
credentials_file,
239+
scopes=scopes,
240+
default_scopes=default_scopes
241+
)
242+
else:
243+
credentials, _ = google.auth.load_credentials_from_file(
244+
credentials_file,
245+
scopes=scopes or default_scopes,
246+
)
214247
elif credentials:
215-
credentials = google.auth.credentials.with_scopes_if_required(credentials, scopes)
248+
# TODO: remove this if/else once google-auth >= 1.25.0 is required
249+
if _GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
250+
credentials = google.auth.credentials.with_scopes_if_required(
251+
credentials,
252+
scopes=scopes,
253+
default_scopes=default_scopes
254+
)
255+
else:
256+
credentials = google.auth.credentials.with_scopes_if_required(
257+
credentials,
258+
scopes=scopes or default_scopes,
259+
)
260+
216261
else:
217-
credentials, _ = google.auth.default(scopes=scopes)
262+
# TODO: remove this if/else once google-auth >= 1.25.0 is required
263+
if _GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
264+
credentials, _ = google.auth.default(scopes=scopes, default_scopes=default_scopes)
265+
else:
266+
credentials, _ = google.auth.default(scopes=scopes or default_scopes)
218267

219268
if quota_project_id and isinstance(credentials, google.auth.credentials.CredentialsWithQuotaProject):
220269
credentials = credentials.with_quota_project(quota_project_id)
221270

222271
request = google.auth.transport.requests.Request()
223272

224273
# Create the metadata plugin for inserting the authorization header.
225-
metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin(
226-
credentials, request
227-
)
274+
275+
# TODO: remove this if/else once google-auth >= 1.25.0 is required
276+
if _GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
277+
metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin(
278+
credentials, request, default_host=default_host,
279+
)
280+
else:
281+
metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin(
282+
credentials, request
283+
)
228284

229285
# Create a set of grpc.CallCredentials using the metadata plugin.
230286
google_auth_credentials = grpc.metadata_call_credentials(metadata_plugin)
@@ -245,6 +301,8 @@ def create_channel(
245301
ssl_credentials=None,
246302
credentials_file=None,
247303
quota_project_id=None,
304+
default_scopes=None,
305+
default_host=None,
248306
**kwargs):
249307
"""Create a secure channel with credentials.
250308
@@ -262,6 +320,9 @@ def create_channel(
262320
:func:`google.auth.load_credentials_from_file`. This argument is
263321
mutually exclusive with credentials.
264322
quota_project_id (str): An optional project to use for billing and quota.
323+
default_scopes (Sequence[str]): Default scopes passed by a Google client
324+
library. Use 'scopes' for user-defined scopes.
325+
default_host (str): The default endpoint. e.g., "pubsub.googleapis.com".
265326
kwargs: Additional key-word args passed to
266327
:func:`grpc_gcp.secure_channel` or :func:`grpc.secure_channel`.
267328
@@ -275,9 +336,11 @@ def create_channel(
275336
composite_credentials = _create_composite_credentials(
276337
credentials=credentials,
277338
credentials_file=credentials_file,
339+
default_scopes=default_scopes,
278340
scopes=scopes,
279341
ssl_credentials=ssl_credentials,
280342
quota_project_id=quota_project_id,
343+
default_host=default_host,
281344
)
282345

283346
if HAS_GRPC_GCP:

google/api_core/grpc_helpers_async.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ def create_channel(
213213
ssl_credentials=None,
214214
credentials_file=None,
215215
quota_project_id=None,
216+
default_scopes=None,
217+
default_host=None,
216218
**kwargs):
217219
"""Create an AsyncIO secure channel with credentials.
218220
@@ -230,6 +232,9 @@ def create_channel(
230232
:func:`google.auth.load_credentials_from_file`. This argument is
231233
mutually exclusive with credentials.
232234
quota_project_id (str): An optional project to use for billing and quota.
235+
default_scopes (Sequence[str]): Default scopes passed by a Google client
236+
library. Use 'scopes' for user-defined scopes.
237+
default_host (str): The default endpoint. e.g., "pubsub.googleapis.com".
233238
kwargs: Additional key-word args passed to :func:`aio.secure_channel`.
234239
235240
Returns:
@@ -243,8 +248,10 @@ def create_channel(
243248
credentials=credentials,
244249
credentials_file=credentials_file,
245250
scopes=scopes,
251+
default_scopes=default_scopes,
246252
ssl_credentials=ssl_credentials,
247253
quota_project_id=quota_project_id,
254+
default_host=default_host
248255
)
249256

250257
return aio.secure_channel(target, composite_credentials, **kwargs)

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"google-auth >= 1.21.1, < 2.0dev",
3535
"requests >= 2.18.0, < 3.0.0dev",
3636
"setuptools >= 40.3.0",
37+
"packaging >= 14.3",
3738
"six >= 1.13.0",
3839
"pytz",
3940
'futures >= 3.2.0; python_version < "3.2"',

testing/constraints-3.6.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ protobuf==3.12.0
1010
google-auth==1.21.1
1111
requests==2.18.0
1212
setuptools==40.3.0
13+
packaging==14.3
1314
six==1.13.0
1415
grpcio==1.29.0
1516
grpcio-gcp==0.2.2

0 commit comments

Comments
 (0)