Skip to content
This repository was 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
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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