Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

feat: support self-signed jwt #107

Closed
wants to merge 12 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@

import abc
import typing
import packaging.version
import pkg_resources

from google import auth # type: ignore
import google.api_core
from google.api_core import exceptions # type: ignore
from google.api_core import gapic_v1 # type: ignore
from google.api_core import retry as retries # type: ignore
Expand All @@ -37,6 +39,17 @@
except pkg_resources.DistributionNotFound:
DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo()

try:
# google.auth.__version__ was added in 1.26.0
_GOOGLE_AUTH_VERSION = auth.__version__
except AttributeError:
try: # try pkg_resources if it is available
_GOOGLE_AUTH_VERSION = pkg_resources.get_distribution("google-auth").version
except pkg_resources.DistributionNotFound: # pragma: NO COVER
_GOOGLE_AUTH_VERSION = None

_API_CORE_VERSION = google.api_core.__version__


class TranslationServiceTransport(abc.ABC):
"""Abstract transport class for TranslationService."""
Expand All @@ -45,14 +58,15 @@ class TranslationServiceTransport(abc.ABC):
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/cloud-translation",
)
DEFAULT_HOST = "translate.googleapis.com"

def __init__(
self,
*,
host: str = "translate.googleapis.com",
host: str = DEFAULT_HOST,
credentials: credentials.Credentials = None,
credentials_file: typing.Optional[str] = None,
scopes: typing.Optional[typing.Sequence[str]] = AUTH_SCOPES,
scopes: typing.Optional[typing.Sequence[str]] = None,
quota_project_id: typing.Optional[str] = None,
client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
**kwargs,
Expand All @@ -69,7 +83,7 @@ def __init__(
credentials_file (Optional[str]): A file with credentials that can
be loaded with :func:`google.auth.load_credentials_from_file`.
This argument is mutually exclusive with credentials.
scope (Optional[Sequence[str]]): A list of scopes.
scopes (Optional[Sequence[str]]): A list of scopes.
quota_project_id (Optional[str]): An optional project to use for billing
and quota.
client_info (google.api_core.gapic_v1.client_info.ClientInfo):
Expand All @@ -83,6 +97,21 @@ def __init__(
host += ":443"
self._host = host

# If a custom API endpoint is set, set scopes to ensure the auth
# library does not used the self-signed JWT flow for service
# accounts
if host.split(":")[0] != self.DEFAULT_HOST and not scopes:
scopes = self.AUTH_SCOPES

# TODO: Remove this if/else once google-auth >= 1.25.0 is required
if _GOOGLE_AUTH_VERSION and (
packaging.version.parse(_GOOGLE_AUTH_VERSION)
>= packaging.version.parse("1.25.0")
):
scopes_kwargs = {"scopes": scopes, "default_scopes": self.AUTH_SCOPES}
else:
scopes_kwargs = {"scopes": scopes or self.AUTH_SCOPES}

# If no credentials are provided, then determine the appropriate
# defaults.
if credentials and credentials_file:
Expand All @@ -92,12 +121,12 @@ def __init__(

if credentials_file is not None:
credentials, _ = auth.load_credentials_from_file(
credentials_file, scopes=scopes, quota_project_id=quota_project_id
credentials_file, **scopes_kwargs, quota_project_id=quota_project_id
)

elif credentials is None:
credentials, _ = auth.default(
scopes=scopes, quota_project_id=quota_project_id
**scopes_kwargs, quota_project_id=quota_project_id
)

# Save the credentials.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@
from google import auth # type: ignore
from google.auth import credentials # type: ignore
from google.auth.transport.grpc import SslCredentials # type: ignore
import packaging
import pkg_resources

import grpc # type: ignore

from google.cloud.translate_v3.types import translation_service
from google.longrunning import operations_pb2 as operations # type: ignore

from .base import TranslationServiceTransport, DEFAULT_CLIENT_INFO
from .base import _GOOGLE_AUTH_VERSION, _API_CORE_VERSION


class TranslationServiceGrpcTransport(TranslationServiceTransport):
Expand Down Expand Up @@ -105,6 +108,21 @@ def __init__(
"""
self._ssl_channel_credentials = ssl_channel_credentials

# If a custom API endpoint is set, set scopes to ensure the auth
# library does not used the self-signed JWT flow for service
# accounts
if host.split(":")[0] != self.DEFAULT_HOST and not scopes:
scopes = self.AUTH_SCOPES

# TODO: Remove this if/else once google-auth >= 1.25.0 is required
if _GOOGLE_AUTH_VERSION and (
packaging.version.parse(_GOOGLE_AUTH_VERSION)
>= packaging.version.parse("1.25.0")
):
scopes_kwargs = {"scopes": scopes, "default_scopes": self.AUTH_SCOPES}
else:
scopes_kwargs = {"scopes": scopes or self.AUTH_SCOPES}

if channel:
# Sanity check: Ensure that channel and credentials are not both
# provided.
Expand All @@ -127,7 +145,7 @@ def __init__(

if credentials is None:
credentials, _ = auth.default(
scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id
**scopes_kwargs, quota_project_id=quota_project_id,
)

# Create SSL credentials with client_cert_source or application
Expand All @@ -146,7 +164,7 @@ def __init__(
credentials=credentials,
credentials_file=credentials_file,
ssl_credentials=ssl_credentials,
scopes=scopes or self.AUTH_SCOPES,
scopes=scopes,
quota_project_id=quota_project_id,
options=[
("grpc.max_send_message_length", -1),
Expand All @@ -159,7 +177,7 @@ def __init__(

if credentials is None:
credentials, _ = auth.default(
scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id
**scopes_kwargs, quota_project_id=quota_project_id,
)

# create a new channel. The provided one is ignored.
Expand All @@ -168,7 +186,7 @@ def __init__(
credentials=credentials,
credentials_file=credentials_file,
ssl_credentials=ssl_channel_credentials,
scopes=scopes or self.AUTH_SCOPES,
scopes=scopes,
quota_project_id=quota_project_id,
options=[
("grpc.max_send_message_length", -1),
Expand All @@ -184,7 +202,7 @@ def __init__(
host=host,
credentials=credentials,
credentials_file=credentials_file,
scopes=scopes or self.AUTH_SCOPES,
scopes=scopes,
quota_project_id=quota_project_id,
client_info=client_info,
)
Expand Down Expand Up @@ -224,13 +242,26 @@ def create_channel(
google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials``
and ``credentials_file`` are passed.
"""
scopes = scopes or cls.AUTH_SCOPES

self_signed_jwt_kwargs = {}

# TODO: Remove this if/else once google-api-core >= 1.26.0 is required
if _API_CORE_VERSION and (
packaging.version.parse(_API_CORE_VERSION)
>= packaging.version.parse("1.26.0")
):
self_signed_jwt_kwargs["default_scopes"] = cls.AUTH_SCOPES
self_signed_jwt_kwargs["scopes"] = scopes
self_signed_jwt_kwargs["default_host"] = cls.DEFAULT_HOST
else:
self_signed_jwt_kwargs["scopes"] = scopes or cls.AUTH_SCOPES

return grpc_helpers.create_channel(
host,
credentials=credentials,
credentials_file=credentials_file,
scopes=scopes,
quota_project_id=quota_project_id,
**self_signed_jwt_kwargs,
**kwargs,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from google import auth # type: ignore
from google.auth import credentials # type: ignore
from google.auth.transport.grpc import SslCredentials # type: ignore
import packaging.version

import grpc # type: ignore
from grpc.experimental import aio # type: ignore
Expand All @@ -32,6 +33,7 @@
from google.longrunning import operations_pb2 as operations # type: ignore

from .base import TranslationServiceTransport, DEFAULT_CLIENT_INFO
from .base import _GOOGLE_AUTH_VERSION, _API_CORE_VERSION
from .grpc import TranslationServiceGrpcTransport


Expand Down Expand Up @@ -82,13 +84,25 @@ def create_channel(
Returns:
aio.Channel: A gRPC AsyncIO channel object.
"""
scopes = scopes or cls.AUTH_SCOPES
self_signed_jwt_kwargs = {}

# TODO: Remove this if/else once google-api-core >= 1.26.0 is required
if _API_CORE_VERSION and (
packaging.version.parse(_API_CORE_VERSION)
>= packaging.version.parse("1.26.0")
):
self_signed_jwt_kwargs["default_scopes"] = cls.AUTH_SCOPES
self_signed_jwt_kwargs["scopes"] = scopes
self_signed_jwt_kwargs["default_host"] = cls.DEFAULT_HOST
else:
self_signed_jwt_kwargs["scopes"] = scopes or cls.AUTH_SCOPES

return grpc_helpers_async.create_channel(
host,
credentials=credentials,
credentials_file=credentials_file,
scopes=scopes,
quota_project_id=quota_project_id,
**self_signed_jwt_kwargs,
**kwargs,
)

Expand Down Expand Up @@ -150,6 +164,20 @@ def __init__(
"""
self._ssl_channel_credentials = ssl_channel_credentials

# If a custom API endpoint is set, set scopes to ensure the auth
# library does not used the self-signed JWT flow for service
# accounts
if host.split(":")[0] != self.DEFAULT_HOST and not scopes:
scopes = self.AUTH_SCOPES

# TODO: Remove this if/else once google-auth >= 1.25.0 is required
if _GOOGLE_AUTH_VERSION and packaging.version.parse(
_GOOGLE_AUTH_VERSION
) >= packaging.version.parse("1.25.0"):
scopes_kwargs = {"scopes": scopes, "default_scopes": self.AUTH_SCOPES}
else:
scopes_kwargs = {"scopes": scopes or self.AUTH_SCOPES}

if channel:
# Sanity check: Ensure that channel and credentials are not both
# provided.
Expand All @@ -172,7 +200,7 @@ def __init__(

if credentials is None:
credentials, _ = auth.default(
scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id
quota_project_id=quota_project_id, **scopes_kwargs
)

# Create SSL credentials with client_cert_source or application
Expand All @@ -191,7 +219,7 @@ def __init__(
credentials=credentials,
credentials_file=credentials_file,
ssl_credentials=ssl_credentials,
scopes=scopes or self.AUTH_SCOPES,
scopes=scopes,
quota_project_id=quota_project_id,
options=[
("grpc.max_send_message_length", -1),
Expand All @@ -204,7 +232,7 @@ def __init__(

if credentials is None:
credentials, _ = auth.default(
scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id
quota_project_id=quota_project_id, **scopes_kwargs,
)

# create a new channel. The provided one is ignored.
Expand All @@ -213,7 +241,7 @@ def __init__(
credentials=credentials,
credentials_file=credentials_file,
ssl_credentials=ssl_channel_credentials,
scopes=scopes or self.AUTH_SCOPES,
scopes=scopes,
quota_project_id=quota_project_id,
options=[
("grpc.max_send_message_length", -1),
Expand All @@ -226,7 +254,7 @@ def __init__(
host=host,
credentials=credentials,
credentials_file=credentials_file,
scopes=scopes or self.AUTH_SCOPES,
scopes=scopes,
quota_project_id=quota_project_id,
client_info=client_info,
)
Expand Down
Loading