From 60e221b010c18f12b156c2e282edc647d178a0f2 Mon Sep 17 00:00:00 2001 From: arithmetic1728 <58957152+arithmetic1728@users.noreply.github.com> Date: Mon, 27 Jul 2020 14:51:57 -0700 Subject: [PATCH] feat!: migrate to microgenerator (#26) --- .coveragerc | 13 +- README.rst | 6 +- UPGRADING.md | 152 ++ docs/UPGRADING.md | 152 ++ docs/credentials_v1/services.rst | 6 + docs/credentials_v1/types.rst | 5 + docs/gapic/v1/api.rst | 6 - docs/gapic/v1/types.rst | 5 - docs/index.rst | 15 +- google/cloud/iam_credentials/__init__.py | 44 + google/cloud/iam_credentials/py.typed | 2 + google/cloud/iam_credentials_v1/__init__.py | 43 +- .../iam_credentials_v1/gapic/__init__.py | 0 .../gapic/iam_credentials_client.py | 590 ------- .../gapic/iam_credentials_client_config.py | 52 - .../gapic/transports/__init__.py | 0 .../iam_credentials_grpc_transport.py | 163 -- .../iam_credentials_v1/proto/__init__.py | 0 .../iam_credentials_v1/proto/common_pb2.py | 861 ---------- .../proto/common_pb2_grpc.py | 3 - .../proto/iamcredentials_pb2.py | 98 -- .../proto/iamcredentials_pb2_grpc.py | 239 --- google/cloud/iam_credentials_v1/py.typed | 2 + .../services}/__init__.py | 14 +- .../services/iam_credentials/__init__.py} | 15 +- .../services/iam_credentials/async_client.py | 583 +++++++ .../services/iam_credentials/client.py | 709 ++++++++ .../iam_credentials/transports/__init__.py | 36 + .../iam_credentials/transports/base.py | 129 ++ .../iam_credentials/transports/grpc.py | 322 ++++ .../transports/grpc_asyncio.py | 322 ++++ google/cloud/iam_credentials_v1/types.py | 50 - .../iam_credentials_v1/types/__init__.py | 39 + .../cloud/iam_credentials_v1/types/common.py | 258 +++ .../types/iamcredentials.py} | 14 +- noxfile.py | 10 +- samples/AUTHORING_GUIDE.md | 1 + samples/CONTRIBUTING.md | 1 + scripts/fixup_credentials_v1_keywords.py | 181 ++ setup.py | 16 +- synth.metadata | 12 +- synth.py | 59 +- .../credentials_v1/test_iam_credentials.py | 1522 +++++++++++++++++ .../v1/test_iam_credentials_client_v1.py | 225 --- 44 files changed, 4573 insertions(+), 2402 deletions(-) create mode 100644 UPGRADING.md create mode 100644 docs/UPGRADING.md create mode 100644 docs/credentials_v1/services.rst create mode 100644 docs/credentials_v1/types.rst delete mode 100644 docs/gapic/v1/api.rst delete mode 100644 docs/gapic/v1/types.rst create mode 100644 google/cloud/iam_credentials/__init__.py create mode 100644 google/cloud/iam_credentials/py.typed delete mode 100644 google/cloud/iam_credentials_v1/gapic/__init__.py delete mode 100644 google/cloud/iam_credentials_v1/gapic/iam_credentials_client.py delete mode 100644 google/cloud/iam_credentials_v1/gapic/iam_credentials_client_config.py delete mode 100644 google/cloud/iam_credentials_v1/gapic/transports/__init__.py delete mode 100644 google/cloud/iam_credentials_v1/gapic/transports/iam_credentials_grpc_transport.py delete mode 100644 google/cloud/iam_credentials_v1/proto/__init__.py delete mode 100644 google/cloud/iam_credentials_v1/proto/common_pb2.py delete mode 100644 google/cloud/iam_credentials_v1/proto/common_pb2_grpc.py delete mode 100644 google/cloud/iam_credentials_v1/proto/iamcredentials_pb2.py delete mode 100644 google/cloud/iam_credentials_v1/proto/iamcredentials_pb2_grpc.py create mode 100644 google/cloud/iam_credentials_v1/py.typed rename google/cloud/{ => iam_credentials_v1/services}/__init__.py (71%) rename google/cloud/{iam_credentials.py => iam_credentials_v1/services/iam_credentials/__init__.py} (73%) create mode 100644 google/cloud/iam_credentials_v1/services/iam_credentials/async_client.py create mode 100644 google/cloud/iam_credentials_v1/services/iam_credentials/client.py create mode 100644 google/cloud/iam_credentials_v1/services/iam_credentials/transports/__init__.py create mode 100644 google/cloud/iam_credentials_v1/services/iam_credentials/transports/base.py create mode 100644 google/cloud/iam_credentials_v1/services/iam_credentials/transports/grpc.py create mode 100644 google/cloud/iam_credentials_v1/services/iam_credentials/transports/grpc_asyncio.py delete mode 100644 google/cloud/iam_credentials_v1/types.py create mode 100644 google/cloud/iam_credentials_v1/types/__init__.py create mode 100644 google/cloud/iam_credentials_v1/types/common.py rename google/{__init__.py => cloud/iam_credentials_v1/types/iamcredentials.py} (71%) create mode 100644 samples/AUTHORING_GUIDE.md create mode 100644 samples/CONTRIBUTING.md create mode 100644 scripts/fixup_credentials_v1_keywords.py create mode 100644 tests/unit/gapic/credentials_v1/test_iam_credentials.py delete mode 100644 tests/unit/gapic/v1/test_iam_credentials_client_v1.py diff --git a/.coveragerc b/.coveragerc index dd39c85..ca48f2f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -21,15 +21,14 @@ branch = True [report] fail_under = 100 show_missing = True +omit = google/cloud/iam_credentials_v1/__init__.py exclude_lines = # Re-enable the standard pragma pragma: NO COVER # Ignore debug-only repr def __repr__ - # Ignore abstract methods - raise NotImplementedError -omit = - */gapic/*.py - */proto/*.py - */core/*.py - */site-packages/*.py \ No newline at end of file + # Ignore pkg_resources exceptions. + # This is added at the module level as a safeguard for if someone + # generates the code and tries to run it without pip installing. This + # makes it virtually impossible to test properly. + except pkg_resources.DistributionNotFound diff --git a/README.rst b/README.rst index dba6b13..20fc83d 100644 --- a/README.rst +++ b/README.rst @@ -51,11 +51,13 @@ dependencies. Supported Python Versions ^^^^^^^^^^^^^^^^^^^^^^^^^ -Python >= 3.5 +Python >= 3.6 Deprecated Python Versions ^^^^^^^^^^^^^^^^^^^^^^^^^^ -Python == 2.7. Python 2.7 support will be removed on January 1, 2020. +Python == 2.7. + +The last version of this library compatible with Python 2.7 is google-cloud-iam==1.0.1. Mac/Linux diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 0000000..7bc4783 --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1,152 @@ +# 2.0.0 Migration Guide + +The 2.0 release of the `google-cloud-iam` client is a significant upgrade based on a [next-gen code generator](https://github.com/googleapis/gapic-generator-python), and includes substantial interface changes. Existing code written for earlier versions of this library will likely require updates to use this version. This document describes the changes that have been made, and what you need to do to update your usage. + +If you experience issues or have questions, please file an [issue](https://github.com/googleapis/python-iam/issues). + +## Supported Python Versions + +> **WARNING**: Breaking change + +The 2.0.0 release requires Python 3.6+. + + +## Method Calls + +> **WARNING**: Breaking change + +Methods expect request objects. We provide a script that will convert most common use cases. + +* Install the library + +```py +python3 -m pip install google-cloud-iam +``` + +* The script `fixup_credentials_v1_keywords.py` is shipped with the library. It expects an input directory (with the code to convert) and an empty destination directory. + +```sh +$ fixup_credentials_v1_keywords.py --input-directory .samples/ --output-directory samples/ +``` + +**Before:** +```py +from google.cloud import iam_credentials + +client = iam_credentials.IAMCredentialsClient() + +access_token = client.generate_access_token( + name, scope, delegates = delegates, lifetime = lifetime +) +``` + + +**After:** +```py +from google.cloud import iam_credentials + +client = iam_credentials.IAMCredentialsClient() + +access_token = client.generate_access_token( + request = {'name': name, 'scope': scope, 'delegates': delegates, 'lifetime': lifetime} +) +``` + +### More Details + +In `google-cloud-iam<2.0.0`, parameters required by the API were positional parameters and optional parameters were keyword parameters. + +**Before:** +```py + def generate_access_token( + self, + name, + scope, + delegates=None, + lifetime=None, + retry=google.api_core.gapic_v1.method.DEFAULT, + timeout=google.api_core.gapic_v1.method.DEFAULT, + metadata=None, + ): +``` + +In the 2.0.0 release, all methods have a single positional parameter `request`. Method docstrings indicate whether a parameter is required or optional. + +Some methods have additional keyword only parameters. The available parameters depend on the [`google.api.method_signature` annotation](https://github.com/googleapis/googleapis/blob/master/google/iam/credentials/v1/iamcredentials.proto#L49) specified by the API producer. + + +**After:** +```py + def generate_access_token( + self, + request: common.GenerateAccessTokenRequest = None, + *, + name: str = None, + scope: Sequence[str] = None, + delegates: Sequence[str] = None, + lifetime: duration.Duration = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> common.GenerateAccessTokenResponse: +``` + +> **NOTE:** The `request` parameter and flattened keyword parameters for the API are mutually exclusive. +> Passing both will result in an error. + + +Both of these calls are valid: + +```py +access_token = client.generate_access_token( + request={ + "name": name, + "scope": scope, + "delegates": delegates, + "lifetime": lifetime + } +) +``` + +```py +access_token = client.generate_access_token( + name, scope, delegates = delegates, lifetime = lifetime +) +``` + +This call is invalid because it mixes `request` with a keyword argument `lifetime`. Executing this code will result in an error. + +```py +access_token = client.generate_access_token( + request={ + "name": name, + "scope": scope, + "delegates": delegates + }, + lifetime = lifetime +) +``` + + + +## Types + + +> **WARNING**: Breaking change + +The submodule `types` has been removed. + +**Before:** +```py +from google.cloud import iam_credentials + +request = iam_credentials.types.GenerateAccessTokenRequest(name="name") +``` + + +**After:** +```py +from google.cloud import iam_credentials + +request = iam_credentials.GenerateAccessTokenRequest(name="name") +``` diff --git a/docs/UPGRADING.md b/docs/UPGRADING.md new file mode 100644 index 0000000..7bc4783 --- /dev/null +++ b/docs/UPGRADING.md @@ -0,0 +1,152 @@ +# 2.0.0 Migration Guide + +The 2.0 release of the `google-cloud-iam` client is a significant upgrade based on a [next-gen code generator](https://github.com/googleapis/gapic-generator-python), and includes substantial interface changes. Existing code written for earlier versions of this library will likely require updates to use this version. This document describes the changes that have been made, and what you need to do to update your usage. + +If you experience issues or have questions, please file an [issue](https://github.com/googleapis/python-iam/issues). + +## Supported Python Versions + +> **WARNING**: Breaking change + +The 2.0.0 release requires Python 3.6+. + + +## Method Calls + +> **WARNING**: Breaking change + +Methods expect request objects. We provide a script that will convert most common use cases. + +* Install the library + +```py +python3 -m pip install google-cloud-iam +``` + +* The script `fixup_credentials_v1_keywords.py` is shipped with the library. It expects an input directory (with the code to convert) and an empty destination directory. + +```sh +$ fixup_credentials_v1_keywords.py --input-directory .samples/ --output-directory samples/ +``` + +**Before:** +```py +from google.cloud import iam_credentials + +client = iam_credentials.IAMCredentialsClient() + +access_token = client.generate_access_token( + name, scope, delegates = delegates, lifetime = lifetime +) +``` + + +**After:** +```py +from google.cloud import iam_credentials + +client = iam_credentials.IAMCredentialsClient() + +access_token = client.generate_access_token( + request = {'name': name, 'scope': scope, 'delegates': delegates, 'lifetime': lifetime} +) +``` + +### More Details + +In `google-cloud-iam<2.0.0`, parameters required by the API were positional parameters and optional parameters were keyword parameters. + +**Before:** +```py + def generate_access_token( + self, + name, + scope, + delegates=None, + lifetime=None, + retry=google.api_core.gapic_v1.method.DEFAULT, + timeout=google.api_core.gapic_v1.method.DEFAULT, + metadata=None, + ): +``` + +In the 2.0.0 release, all methods have a single positional parameter `request`. Method docstrings indicate whether a parameter is required or optional. + +Some methods have additional keyword only parameters. The available parameters depend on the [`google.api.method_signature` annotation](https://github.com/googleapis/googleapis/blob/master/google/iam/credentials/v1/iamcredentials.proto#L49) specified by the API producer. + + +**After:** +```py + def generate_access_token( + self, + request: common.GenerateAccessTokenRequest = None, + *, + name: str = None, + scope: Sequence[str] = None, + delegates: Sequence[str] = None, + lifetime: duration.Duration = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> common.GenerateAccessTokenResponse: +``` + +> **NOTE:** The `request` parameter and flattened keyword parameters for the API are mutually exclusive. +> Passing both will result in an error. + + +Both of these calls are valid: + +```py +access_token = client.generate_access_token( + request={ + "name": name, + "scope": scope, + "delegates": delegates, + "lifetime": lifetime + } +) +``` + +```py +access_token = client.generate_access_token( + name, scope, delegates = delegates, lifetime = lifetime +) +``` + +This call is invalid because it mixes `request` with a keyword argument `lifetime`. Executing this code will result in an error. + +```py +access_token = client.generate_access_token( + request={ + "name": name, + "scope": scope, + "delegates": delegates + }, + lifetime = lifetime +) +``` + + + +## Types + + +> **WARNING**: Breaking change + +The submodule `types` has been removed. + +**Before:** +```py +from google.cloud import iam_credentials + +request = iam_credentials.types.GenerateAccessTokenRequest(name="name") +``` + + +**After:** +```py +from google.cloud import iam_credentials + +request = iam_credentials.GenerateAccessTokenRequest(name="name") +``` diff --git a/docs/credentials_v1/services.rst b/docs/credentials_v1/services.rst new file mode 100644 index 0000000..13d1202 --- /dev/null +++ b/docs/credentials_v1/services.rst @@ -0,0 +1,6 @@ +Services for Google Iam Credentials v1 API +========================================== + +.. automodule:: google.cloud.iam_credentials_v1.services.iam_credentials + :members: + :inherited-members: diff --git a/docs/credentials_v1/types.rst b/docs/credentials_v1/types.rst new file mode 100644 index 0000000..a21388a --- /dev/null +++ b/docs/credentials_v1/types.rst @@ -0,0 +1,5 @@ +Types for Google Iam Credentials v1 API +======================================= + +.. automodule:: google.cloud.iam_credentials_v1.types + :members: diff --git a/docs/gapic/v1/api.rst b/docs/gapic/v1/api.rst deleted file mode 100644 index 8983900..0000000 --- a/docs/gapic/v1/api.rst +++ /dev/null @@ -1,6 +0,0 @@ -Client for IAM Service Account Credentials API -============================================== - -.. automodule:: google.cloud.iam_credentials_v1 - :members: - :inherited-members: \ No newline at end of file diff --git a/docs/gapic/v1/types.rst b/docs/gapic/v1/types.rst deleted file mode 100644 index a80cc35..0000000 --- a/docs/gapic/v1/types.rst +++ /dev/null @@ -1,5 +0,0 @@ -Types for IAM Service Account Credentials API Client -==================================================== - -.. automodule:: google.cloud.iam_credentials_v1.types - :members: \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index d895c74..ef44a2d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -8,8 +8,19 @@ API Reference .. toctree:: :maxdepth: 2 - gapic/v1/api - gapic/v1/types + credentials_v1/services + credentials_v1/types + + +Migration Guide +--------------- + +See the guide below for instructions on migrating to the 2.x release of this library. + +.. toctree:: + :maxdepth: 2 + + UPGRADING Changelog diff --git a/google/cloud/iam_credentials/__init__.py b/google/cloud/iam_credentials/__init__.py new file mode 100644 index 0000000..b9e9538 --- /dev/null +++ b/google/cloud/iam_credentials/__init__.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from google.cloud.iam_credentials_v1.services.iam_credentials.async_client import ( + IAMCredentialsAsyncClient, +) +from google.cloud.iam_credentials_v1.services.iam_credentials.client import ( + IAMCredentialsClient, +) +from google.cloud.iam_credentials_v1.types.common import GenerateAccessTokenRequest +from google.cloud.iam_credentials_v1.types.common import GenerateAccessTokenResponse +from google.cloud.iam_credentials_v1.types.common import GenerateIdTokenRequest +from google.cloud.iam_credentials_v1.types.common import GenerateIdTokenResponse +from google.cloud.iam_credentials_v1.types.common import SignBlobRequest +from google.cloud.iam_credentials_v1.types.common import SignBlobResponse +from google.cloud.iam_credentials_v1.types.common import SignJwtRequest +from google.cloud.iam_credentials_v1.types.common import SignJwtResponse + +__all__ = ( + "GenerateAccessTokenRequest", + "GenerateAccessTokenResponse", + "GenerateIdTokenRequest", + "GenerateIdTokenResponse", + "IAMCredentialsAsyncClient", + "IAMCredentialsClient", + "SignBlobRequest", + "SignBlobResponse", + "SignJwtRequest", + "SignJwtResponse", +) diff --git a/google/cloud/iam_credentials/py.typed b/google/cloud/iam_credentials/py.typed new file mode 100644 index 0000000..4d9bf55 --- /dev/null +++ b/google/cloud/iam_credentials/py.typed @@ -0,0 +1,2 @@ +# Marker file for PEP 561. +# The google-iam-credentials package uses inline types. diff --git a/google/cloud/iam_credentials_v1/__init__.py b/google/cloud/iam_credentials_v1/__init__.py index 11c380f..aaf7fed 100644 --- a/google/cloud/iam_credentials_v1/__init__.py +++ b/google/cloud/iam_credentials_v1/__init__.py @@ -1,42 +1,39 @@ # -*- coding: utf-8 -*- -# + # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# - -from __future__ import absolute_import -import sys -import warnings - -from google.cloud.iam_credentials_v1 import types -from google.cloud.iam_credentials_v1.gapic import iam_credentials_client - - -if sys.version_info[:2] == (2, 7): - message = ( - "A future version of this library will drop support for Python 2.7. " - "More details about Python 2 support for Google Cloud Client Libraries " - "can be found at https://cloud.google.com/python/docs/python2-sunset/" - ) - warnings.warn(message, DeprecationWarning) - - -class IAMCredentialsClient(iam_credentials_client.IAMCredentialsClient): - __doc__ = iam_credentials_client.IAMCredentialsClient.__doc__ +from .services.iam_credentials import IAMCredentialsClient +from .types.common import GenerateAccessTokenRequest +from .types.common import GenerateAccessTokenResponse +from .types.common import GenerateIdTokenRequest +from .types.common import GenerateIdTokenResponse +from .types.common import SignBlobRequest +from .types.common import SignBlobResponse +from .types.common import SignJwtRequest +from .types.common import SignJwtResponse __all__ = ( - "types", + "GenerateAccessTokenRequest", + "GenerateAccessTokenResponse", + "GenerateIdTokenRequest", + "GenerateIdTokenResponse", + "SignBlobRequest", + "SignBlobResponse", + "SignJwtRequest", + "SignJwtResponse", "IAMCredentialsClient", ) diff --git a/google/cloud/iam_credentials_v1/gapic/__init__.py b/google/cloud/iam_credentials_v1/gapic/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/google/cloud/iam_credentials_v1/gapic/iam_credentials_client.py b/google/cloud/iam_credentials_v1/gapic/iam_credentials_client.py deleted file mode 100644 index d4aacc7..0000000 --- a/google/cloud/iam_credentials_v1/gapic/iam_credentials_client.py +++ /dev/null @@ -1,590 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Accesses the google.iam.credentials.v1 IAMCredentials API.""" - -import pkg_resources -import warnings - -from google.oauth2 import service_account -import google.api_core.client_options -import google.api_core.gapic_v1.client_info -import google.api_core.gapic_v1.config -import google.api_core.gapic_v1.method -import google.api_core.gapic_v1.routing_header -import google.api_core.grpc_helpers -import google.api_core.path_template -import grpc - -from google.cloud.iam_credentials_v1.gapic import iam_credentials_client_config -from google.cloud.iam_credentials_v1.gapic.transports import ( - iam_credentials_grpc_transport, -) -from google.cloud.iam_credentials_v1.proto import common_pb2 -from google.cloud.iam_credentials_v1.proto import iamcredentials_pb2_grpc -from google.protobuf import duration_pb2 - - -_GAPIC_LIBRARY_VERSION = pkg_resources.get_distribution("google-cloud-iam",).version - - -class IAMCredentialsClient(object): - """ - A service account is a special type of Google account that belongs to your - application or a virtual machine (VM), instead of to an individual end user. - Your application assumes the identity of the service account to call Google - APIs, so that the users aren't directly involved. - - Service account credentials are used to temporarily assume the identity - of the service account. Supported credential types include OAuth 2.0 access - tokens, OpenID Connect ID tokens, self-signed JSON Web Tokens (JWTs), and - more. - """ - - SERVICE_ADDRESS = "iamcredentials.googleapis.com:443" - """The default address of the service.""" - - # The name of the interface for this client. This is the key used to - # find the method configuration in the client_config dictionary. - _INTERFACE_NAME = "google.iam.credentials.v1.IAMCredentials" - - @classmethod - def from_service_account_file(cls, filename, *args, **kwargs): - """Creates an instance of this client using the provided credentials - file. - - Args: - filename (str): The path to the service account private key json - file. - args: Additional arguments to pass to the constructor. - kwargs: Additional arguments to pass to the constructor. - - Returns: - IAMCredentialsClient: The constructed client. - """ - credentials = service_account.Credentials.from_service_account_file(filename) - kwargs["credentials"] = credentials - return cls(*args, **kwargs) - - from_service_account_json = from_service_account_file - - @classmethod - def service_account_path(cls, project, service_account): - """Return a fully-qualified service_account string.""" - return google.api_core.path_template.expand( - "projects/{project}/serviceAccounts/{service_account}", - project=project, - service_account=service_account, - ) - - def __init__( - self, - transport=None, - channel=None, - credentials=None, - client_config=None, - client_info=None, - client_options=None, - ): - """Constructor. - - Args: - transport (Union[~.IamCredentialsGrpcTransport, - Callable[[~.Credentials, type], ~.IamCredentialsGrpcTransport]): A transport - instance, responsible for actually making the API calls. - The default transport uses the gRPC protocol. - This argument may also be a callable which returns a - transport instance. Callables will be sent the credentials - as the first argument and the default transport class as - the second argument. - channel (grpc.Channel): DEPRECATED. A ``Channel`` instance - through which to make calls. This argument is mutually exclusive - with ``credentials``; providing both will raise an exception. - credentials (google.auth.credentials.Credentials): The - authorization credentials to attach to requests. These - credentials identify this application to the service. If none - are specified, the client will attempt to ascertain the - credentials from the environment. - This argument is mutually exclusive with providing a - transport instance to ``transport``; doing so will raise - an exception. - client_config (dict): DEPRECATED. A dictionary of call options for - each method. If not specified, the default configuration is used. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing - your own client library. - client_options (Union[dict, google.api_core.client_options.ClientOptions]): - Client options used to set user options on the client. API Endpoint - should be set through client_options. - """ - # Raise deprecation warnings for things we want to go away. - if client_config is not None: - warnings.warn( - "The `client_config` argument is deprecated.", - PendingDeprecationWarning, - stacklevel=2, - ) - else: - client_config = iam_credentials_client_config.config - - if channel: - warnings.warn( - "The `channel` argument is deprecated; use " "`transport` instead.", - PendingDeprecationWarning, - stacklevel=2, - ) - - api_endpoint = self.SERVICE_ADDRESS - if client_options: - if type(client_options) == dict: - client_options = google.api_core.client_options.from_dict( - client_options - ) - if client_options.api_endpoint: - api_endpoint = client_options.api_endpoint - - # Instantiate the transport. - # The transport is responsible for handling serialization and - # deserialization and actually sending data to the service. - if transport: - if callable(transport): - self.transport = transport( - credentials=credentials, - default_class=iam_credentials_grpc_transport.IamCredentialsGrpcTransport, - address=api_endpoint, - ) - else: - if credentials: - raise ValueError( - "Received both a transport instance and " - "credentials; these are mutually exclusive." - ) - self.transport = transport - else: - self.transport = iam_credentials_grpc_transport.IamCredentialsGrpcTransport( - address=api_endpoint, channel=channel, credentials=credentials, - ) - - if client_info is None: - client_info = google.api_core.gapic_v1.client_info.ClientInfo( - gapic_version=_GAPIC_LIBRARY_VERSION, - ) - else: - client_info.gapic_version = _GAPIC_LIBRARY_VERSION - self._client_info = client_info - - # Parse out the default settings for retry and timeout for each RPC - # from the client configuration. - # (Ordinarily, these are the defaults specified in the `*_config.py` - # file next to this one.) - self._method_configs = google.api_core.gapic_v1.config.parse_method_configs( - client_config["interfaces"][self._INTERFACE_NAME], - ) - - # Save a dictionary of cached API call functions. - # These are the actual callables which invoke the proper - # transport methods, wrapped with `wrap_method` to add retry, - # timeout, and the like. - self._inner_api_calls = {} - - # Service calls - def generate_access_token( - self, - name, - scope, - delegates=None, - lifetime=None, - retry=google.api_core.gapic_v1.method.DEFAULT, - timeout=google.api_core.gapic_v1.method.DEFAULT, - metadata=None, - ): - """ - Generates an OAuth 2.0 access token for a service account. - - Example: - >>> from google.cloud import iam_credentials_v1 - >>> - >>> client = iam_credentials_v1.IAMCredentialsClient() - >>> - >>> name = client.service_account_path('[PROJECT]', '[SERVICE_ACCOUNT]') - >>> - >>> # TODO: Initialize `scope`: - >>> scope = [] - >>> - >>> response = client.generate_access_token(name, scope) - - Args: - name (str): Required. The resource name of the service account for which the - credentials are requested, in the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. The ``-`` - wildcard character is required; replacing it with a project ID is - invalid. - scope (list[str]): Required. Code to identify the scopes to be included in the OAuth 2.0 access token. - See https://developers.google.com/identity/protocols/googlescopes for more - information. - At least one value required. - delegates (list[str]): The sequence of service accounts in a delegation chain. Each service - account must be granted the ``roles/iam.serviceAccountTokenCreator`` - role on its next service account in the chain. The last service account - in the chain must be granted the - ``roles/iam.serviceAccountTokenCreator`` role on the service account - that is specified in the ``name`` field of the request. - - The delegates must have the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. The ``-`` - wildcard character is required; replacing it with a project ID is - invalid. - lifetime (Union[dict, ~google.cloud.iam_credentials_v1.types.Duration]): The desired lifetime duration of the access token in seconds. - Must be set to a value less than or equal to 3600 (1 hour). If a value is - not specified, the token's lifetime will be set to a default value of one - hour. - - If a dict is provided, it must be of the same form as the protobuf - message :class:`~google.cloud.iam_credentials_v1.types.Duration` - retry (Optional[google.api_core.retry.Retry]): A retry object used - to retry requests. If ``None`` is specified, requests will - be retried using a default configuration. - timeout (Optional[float]): The amount of time, in seconds, to wait - for the request to complete. Note that if ``retry`` is - specified, the timeout applies to each individual attempt. - metadata (Optional[Sequence[Tuple[str, str]]]): Additional metadata - that is provided to the method. - - Returns: - A :class:`~google.cloud.iam_credentials_v1.types.GenerateAccessTokenResponse` instance. - - Raises: - google.api_core.exceptions.GoogleAPICallError: If the request - failed for any reason. - google.api_core.exceptions.RetryError: If the request failed due - to a retryable error and retry attempts failed. - ValueError: If the parameters are invalid. - """ - # Wrap the transport method to add retry and timeout logic. - if "generate_access_token" not in self._inner_api_calls: - self._inner_api_calls[ - "generate_access_token" - ] = google.api_core.gapic_v1.method.wrap_method( - self.transport.generate_access_token, - default_retry=self._method_configs["GenerateAccessToken"].retry, - default_timeout=self._method_configs["GenerateAccessToken"].timeout, - client_info=self._client_info, - ) - - request = common_pb2.GenerateAccessTokenRequest( - name=name, scope=scope, delegates=delegates, lifetime=lifetime, - ) - if metadata is None: - metadata = [] - metadata = list(metadata) - try: - routing_header = [("name", name)] - except AttributeError: - pass - else: - routing_metadata = google.api_core.gapic_v1.routing_header.to_grpc_metadata( - routing_header - ) - metadata.append(routing_metadata) - - return self._inner_api_calls["generate_access_token"]( - request, retry=retry, timeout=timeout, metadata=metadata - ) - - def generate_id_token( - self, - name, - audience, - delegates=None, - include_email=None, - retry=google.api_core.gapic_v1.method.DEFAULT, - timeout=google.api_core.gapic_v1.method.DEFAULT, - metadata=None, - ): - """ - Generates an OpenID Connect ID token for a service account. - - Example: - >>> from google.cloud import iam_credentials_v1 - >>> - >>> client = iam_credentials_v1.IAMCredentialsClient() - >>> - >>> name = client.service_account_path('[PROJECT]', '[SERVICE_ACCOUNT]') - >>> - >>> # TODO: Initialize `audience`: - >>> audience = '' - >>> - >>> response = client.generate_id_token(name, audience) - - Args: - name (str): Required. The resource name of the service account for which the - credentials are requested, in the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. The ``-`` - wildcard character is required; replacing it with a project ID is - invalid. - audience (str): Required. The audience for the token, such as the API or account that this token - grants access to. - delegates (list[str]): The sequence of service accounts in a delegation chain. Each service - account must be granted the ``roles/iam.serviceAccountTokenCreator`` - role on its next service account in the chain. The last service account - in the chain must be granted the - ``roles/iam.serviceAccountTokenCreator`` role on the service account - that is specified in the ``name`` field of the request. - - The delegates must have the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. The ``-`` - wildcard character is required; replacing it with a project ID is - invalid. - include_email (bool): Include the service account email in the token. If set to ``true``, - the token will contain ``email`` and ``email_verified`` claims. - retry (Optional[google.api_core.retry.Retry]): A retry object used - to retry requests. If ``None`` is specified, requests will - be retried using a default configuration. - timeout (Optional[float]): The amount of time, in seconds, to wait - for the request to complete. Note that if ``retry`` is - specified, the timeout applies to each individual attempt. - metadata (Optional[Sequence[Tuple[str, str]]]): Additional metadata - that is provided to the method. - - Returns: - A :class:`~google.cloud.iam_credentials_v1.types.GenerateIdTokenResponse` instance. - - Raises: - google.api_core.exceptions.GoogleAPICallError: If the request - failed for any reason. - google.api_core.exceptions.RetryError: If the request failed due - to a retryable error and retry attempts failed. - ValueError: If the parameters are invalid. - """ - # Wrap the transport method to add retry and timeout logic. - if "generate_id_token" not in self._inner_api_calls: - self._inner_api_calls[ - "generate_id_token" - ] = google.api_core.gapic_v1.method.wrap_method( - self.transport.generate_id_token, - default_retry=self._method_configs["GenerateIdToken"].retry, - default_timeout=self._method_configs["GenerateIdToken"].timeout, - client_info=self._client_info, - ) - - request = common_pb2.GenerateIdTokenRequest( - name=name, - audience=audience, - delegates=delegates, - include_email=include_email, - ) - if metadata is None: - metadata = [] - metadata = list(metadata) - try: - routing_header = [("name", name)] - except AttributeError: - pass - else: - routing_metadata = google.api_core.gapic_v1.routing_header.to_grpc_metadata( - routing_header - ) - metadata.append(routing_metadata) - - return self._inner_api_calls["generate_id_token"]( - request, retry=retry, timeout=timeout, metadata=metadata - ) - - def sign_blob( - self, - name, - payload, - delegates=None, - retry=google.api_core.gapic_v1.method.DEFAULT, - timeout=google.api_core.gapic_v1.method.DEFAULT, - metadata=None, - ): - """ - Signs a blob using a service account's system-managed private key. - - Example: - >>> from google.cloud import iam_credentials_v1 - >>> - >>> client = iam_credentials_v1.IAMCredentialsClient() - >>> - >>> name = client.service_account_path('[PROJECT]', '[SERVICE_ACCOUNT]') - >>> - >>> # TODO: Initialize `payload`: - >>> payload = b'' - >>> - >>> response = client.sign_blob(name, payload) - - Args: - name (str): Required. The resource name of the service account for which the - credentials are requested, in the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. The ``-`` - wildcard character is required; replacing it with a project ID is - invalid. - payload (bytes): Required. The bytes to sign. - delegates (list[str]): The sequence of service accounts in a delegation chain. Each service - account must be granted the ``roles/iam.serviceAccountTokenCreator`` - role on its next service account in the chain. The last service account - in the chain must be granted the - ``roles/iam.serviceAccountTokenCreator`` role on the service account - that is specified in the ``name`` field of the request. - - The delegates must have the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. The ``-`` - wildcard character is required; replacing it with a project ID is - invalid. - retry (Optional[google.api_core.retry.Retry]): A retry object used - to retry requests. If ``None`` is specified, requests will - be retried using a default configuration. - timeout (Optional[float]): The amount of time, in seconds, to wait - for the request to complete. Note that if ``retry`` is - specified, the timeout applies to each individual attempt. - metadata (Optional[Sequence[Tuple[str, str]]]): Additional metadata - that is provided to the method. - - Returns: - A :class:`~google.cloud.iam_credentials_v1.types.SignBlobResponse` instance. - - Raises: - google.api_core.exceptions.GoogleAPICallError: If the request - failed for any reason. - google.api_core.exceptions.RetryError: If the request failed due - to a retryable error and retry attempts failed. - ValueError: If the parameters are invalid. - """ - # Wrap the transport method to add retry and timeout logic. - if "sign_blob" not in self._inner_api_calls: - self._inner_api_calls[ - "sign_blob" - ] = google.api_core.gapic_v1.method.wrap_method( - self.transport.sign_blob, - default_retry=self._method_configs["SignBlob"].retry, - default_timeout=self._method_configs["SignBlob"].timeout, - client_info=self._client_info, - ) - - request = common_pb2.SignBlobRequest( - name=name, payload=payload, delegates=delegates, - ) - if metadata is None: - metadata = [] - metadata = list(metadata) - try: - routing_header = [("name", name)] - except AttributeError: - pass - else: - routing_metadata = google.api_core.gapic_v1.routing_header.to_grpc_metadata( - routing_header - ) - metadata.append(routing_metadata) - - return self._inner_api_calls["sign_blob"]( - request, retry=retry, timeout=timeout, metadata=metadata - ) - - def sign_jwt( - self, - name, - payload, - delegates=None, - retry=google.api_core.gapic_v1.method.DEFAULT, - timeout=google.api_core.gapic_v1.method.DEFAULT, - metadata=None, - ): - """ - Signs a JWT using a service account's system-managed private key. - - Example: - >>> from google.cloud import iam_credentials_v1 - >>> - >>> client = iam_credentials_v1.IAMCredentialsClient() - >>> - >>> name = client.service_account_path('[PROJECT]', '[SERVICE_ACCOUNT]') - >>> - >>> # TODO: Initialize `payload`: - >>> payload = '' - >>> - >>> response = client.sign_jwt(name, payload) - - Args: - name (str): Required. The resource name of the service account for which the - credentials are requested, in the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. The ``-`` - wildcard character is required; replacing it with a project ID is - invalid. - payload (str): Required. The JWT payload to sign: a JSON object that contains a JWT Claims Set. - delegates (list[str]): The sequence of service accounts in a delegation chain. Each service - account must be granted the ``roles/iam.serviceAccountTokenCreator`` - role on its next service account in the chain. The last service account - in the chain must be granted the - ``roles/iam.serviceAccountTokenCreator`` role on the service account - that is specified in the ``name`` field of the request. - - The delegates must have the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. The ``-`` - wildcard character is required; replacing it with a project ID is - invalid. - retry (Optional[google.api_core.retry.Retry]): A retry object used - to retry requests. If ``None`` is specified, requests will - be retried using a default configuration. - timeout (Optional[float]): The amount of time, in seconds, to wait - for the request to complete. Note that if ``retry`` is - specified, the timeout applies to each individual attempt. - metadata (Optional[Sequence[Tuple[str, str]]]): Additional metadata - that is provided to the method. - - Returns: - A :class:`~google.cloud.iam_credentials_v1.types.SignJwtResponse` instance. - - Raises: - google.api_core.exceptions.GoogleAPICallError: If the request - failed for any reason. - google.api_core.exceptions.RetryError: If the request failed due - to a retryable error and retry attempts failed. - ValueError: If the parameters are invalid. - """ - # Wrap the transport method to add retry and timeout logic. - if "sign_jwt" not in self._inner_api_calls: - self._inner_api_calls[ - "sign_jwt" - ] = google.api_core.gapic_v1.method.wrap_method( - self.transport.sign_jwt, - default_retry=self._method_configs["SignJwt"].retry, - default_timeout=self._method_configs["SignJwt"].timeout, - client_info=self._client_info, - ) - - request = common_pb2.SignJwtRequest( - name=name, payload=payload, delegates=delegates, - ) - if metadata is None: - metadata = [] - metadata = list(metadata) - try: - routing_header = [("name", name)] - except AttributeError: - pass - else: - routing_metadata = google.api_core.gapic_v1.routing_header.to_grpc_metadata( - routing_header - ) - metadata.append(routing_metadata) - - return self._inner_api_calls["sign_jwt"]( - request, retry=retry, timeout=timeout, metadata=metadata - ) diff --git a/google/cloud/iam_credentials_v1/gapic/iam_credentials_client_config.py b/google/cloud/iam_credentials_v1/gapic/iam_credentials_client_config.py deleted file mode 100644 index 5c4cbe1..0000000 --- a/google/cloud/iam_credentials_v1/gapic/iam_credentials_client_config.py +++ /dev/null @@ -1,52 +0,0 @@ -config = { - "interfaces": { - "google.iam.credentials.v1.IAMCredentials": { - "retry_codes": { - "retry_policy_1_codes": ["UNAVAILABLE", "DEADLINE_EXCEEDED"], - "no_retry_codes": [], - }, - "retry_params": { - "retry_policy_1_params": { - "initial_retry_delay_millis": 100, - "retry_delay_multiplier": 1.3, - "max_retry_delay_millis": 60000, - "initial_rpc_timeout_millis": 60000, - "rpc_timeout_multiplier": 1.0, - "max_rpc_timeout_millis": 60000, - "total_timeout_millis": 60000, - }, - "no_retry_params": { - "initial_retry_delay_millis": 0, - "retry_delay_multiplier": 0.0, - "max_retry_delay_millis": 0, - "initial_rpc_timeout_millis": 0, - "rpc_timeout_multiplier": 1.0, - "max_rpc_timeout_millis": 0, - "total_timeout_millis": 0, - }, - }, - "methods": { - "GenerateAccessToken": { - "timeout_millis": 60000, - "retry_codes_name": "retry_policy_1_codes", - "retry_params_name": "retry_policy_1_params", - }, - "GenerateIdToken": { - "timeout_millis": 60000, - "retry_codes_name": "retry_policy_1_codes", - "retry_params_name": "retry_policy_1_params", - }, - "SignBlob": { - "timeout_millis": 60000, - "retry_codes_name": "retry_policy_1_codes", - "retry_params_name": "retry_policy_1_params", - }, - "SignJwt": { - "timeout_millis": 60000, - "retry_codes_name": "retry_policy_1_codes", - "retry_params_name": "retry_policy_1_params", - }, - }, - } - } -} diff --git a/google/cloud/iam_credentials_v1/gapic/transports/__init__.py b/google/cloud/iam_credentials_v1/gapic/transports/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/google/cloud/iam_credentials_v1/gapic/transports/iam_credentials_grpc_transport.py b/google/cloud/iam_credentials_v1/gapic/transports/iam_credentials_grpc_transport.py deleted file mode 100644 index 6363037..0000000 --- a/google/cloud/iam_credentials_v1/gapic/transports/iam_credentials_grpc_transport.py +++ /dev/null @@ -1,163 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -import google.api_core.grpc_helpers - -from google.cloud.iam_credentials_v1.proto import iamcredentials_pb2_grpc - - -class IamCredentialsGrpcTransport(object): - """gRPC transport class providing stubs for - google.iam.credentials.v1 IAMCredentials API. - - The transport provides access to the raw gRPC stubs, - which can be used to take advantage of advanced - features of gRPC. - """ - - # The scopes needed to make gRPC calls to all of the methods defined - # in this service. - _OAUTH_SCOPES = ("https://www.googleapis.com/auth/cloud-platform",) - - def __init__( - self, - channel=None, - credentials=None, - address="iamcredentials.googleapis.com:443", - ): - """Instantiate the transport class. - - Args: - channel (grpc.Channel): A ``Channel`` instance through - which to make calls. This argument is mutually exclusive - with ``credentials``; providing both will raise an exception. - credentials (google.auth.credentials.Credentials): The - authorization credentials to attach to requests. These - credentials identify this application to the service. If none - are specified, the client will attempt to ascertain the - credentials from the environment. - address (str): The address where the service is hosted. - """ - # If both `channel` and `credentials` are specified, raise an - # exception (channels come with credentials baked in already). - if channel is not None and credentials is not None: - raise ValueError( - "The `channel` and `credentials` arguments are mutually " "exclusive.", - ) - - # Create the channel. - if channel is None: - channel = self.create_channel( - address=address, - credentials=credentials, - options={ - "grpc.max_send_message_length": -1, - "grpc.max_receive_message_length": -1, - }.items(), - ) - - self._channel = channel - - # gRPC uses objects called "stubs" that are bound to the - # channel and provide a basic method for each RPC. - self._stubs = { - "iam_credentials_stub": iamcredentials_pb2_grpc.IAMCredentialsStub(channel), - } - - @classmethod - def create_channel( - cls, address="iamcredentials.googleapis.com:443", credentials=None, **kwargs - ): - """Create and return a gRPC channel object. - - Args: - address (str): The host for the channel to use. - credentials (~.Credentials): The - authorization credentials to attach to requests. These - credentials identify this application to the service. If - none are specified, the client will attempt to ascertain - the credentials from the environment. - kwargs (dict): Keyword arguments, which are passed to the - channel creation. - - Returns: - grpc.Channel: A gRPC channel object. - """ - return google.api_core.grpc_helpers.create_channel( - address, credentials=credentials, scopes=cls._OAUTH_SCOPES, **kwargs - ) - - @property - def channel(self): - """The gRPC channel used by the transport. - - Returns: - grpc.Channel: A gRPC channel object. - """ - return self._channel - - @property - def generate_access_token(self): - """Return the gRPC stub for :meth:`IAMCredentialsClient.generate_access_token`. - - Generates an OAuth 2.0 access token for a service account. - - Returns: - Callable: A callable which accepts the appropriate - deserialized request object and returns a - deserialized response object. - """ - return self._stubs["iam_credentials_stub"].GenerateAccessToken - - @property - def generate_id_token(self): - """Return the gRPC stub for :meth:`IAMCredentialsClient.generate_id_token`. - - Generates an OpenID Connect ID token for a service account. - - Returns: - Callable: A callable which accepts the appropriate - deserialized request object and returns a - deserialized response object. - """ - return self._stubs["iam_credentials_stub"].GenerateIdToken - - @property - def sign_blob(self): - """Return the gRPC stub for :meth:`IAMCredentialsClient.sign_blob`. - - Signs a blob using a service account's system-managed private key. - - Returns: - Callable: A callable which accepts the appropriate - deserialized request object and returns a - deserialized response object. - """ - return self._stubs["iam_credentials_stub"].SignBlob - - @property - def sign_jwt(self): - """Return the gRPC stub for :meth:`IAMCredentialsClient.sign_jwt`. - - Signs a JWT using a service account's system-managed private key. - - Returns: - Callable: A callable which accepts the appropriate - deserialized request object and returns a - deserialized response object. - """ - return self._stubs["iam_credentials_stub"].SignJwt diff --git a/google/cloud/iam_credentials_v1/proto/__init__.py b/google/cloud/iam_credentials_v1/proto/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/google/cloud/iam_credentials_v1/proto/common_pb2.py b/google/cloud/iam_credentials_v1/proto/common_pb2.py deleted file mode 100644 index af6620b..0000000 --- a/google/cloud/iam_credentials_v1/proto/common_pb2.py +++ /dev/null @@ -1,861 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: google/cloud/iam_credentials_v1/proto/common.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from google.api import field_behavior_pb2 as google_dot_api_dot_field__behavior__pb2 -from google.api import resource_pb2 as google_dot_api_dot_resource__pb2 -from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 -from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name="google/cloud/iam_credentials_v1/proto/common.proto", - package="google.iam.credentials.v1", - syntax="proto3", - serialized_options=b"\n#com.google.cloud.iam.credentials.v1B\031IAMCredentialsCommonProtoP\001ZDgoogle.golang.org/genproto/googleapis/iam/credentials/v1;credentials\370\001\001\352AY\n!iam.googleapis.com/ServiceAccount\0224projects/{project}/serviceAccounts/{service_account}", - create_key=_descriptor._internal_create_key, - serialized_pb=b'\n2google/cloud/iam_credentials_v1/proto/common.proto\x12\x19google.iam.credentials.v1\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto"\xa9\x01\n\x1aGenerateAccessTokenRequest\x12\x37\n\x04name\x18\x01 \x01(\tB)\xe0\x41\x02\xfa\x41#\n!iam.googleapis.com/ServiceAccount\x12\x11\n\tdelegates\x18\x02 \x03(\t\x12\x12\n\x05scope\x18\x04 \x03(\tB\x03\xe0\x41\x02\x12+\n\x08lifetime\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration"d\n\x1bGenerateAccessTokenResponse\x12\x14\n\x0c\x61\x63\x63\x65ss_token\x18\x01 \x01(\t\x12/\n\x0b\x65xpire_time\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"s\n\x0fSignBlobRequest\x12\x37\n\x04name\x18\x01 \x01(\tB)\xe0\x41\x02\xfa\x41#\n!iam.googleapis.com/ServiceAccount\x12\x11\n\tdelegates\x18\x03 \x03(\t\x12\x14\n\x07payload\x18\x05 \x01(\x0c\x42\x03\xe0\x41\x02"7\n\x10SignBlobResponse\x12\x0e\n\x06key_id\x18\x01 \x01(\t\x12\x13\n\x0bsigned_blob\x18\x04 \x01(\x0c"r\n\x0eSignJwtRequest\x12\x37\n\x04name\x18\x01 \x01(\tB)\xe0\x41\x02\xfa\x41#\n!iam.googleapis.com/ServiceAccount\x12\x11\n\tdelegates\x18\x03 \x03(\t\x12\x14\n\x07payload\x18\x05 \x01(\tB\x03\xe0\x41\x02"5\n\x0fSignJwtResponse\x12\x0e\n\x06key_id\x18\x01 \x01(\t\x12\x12\n\nsigned_jwt\x18\x02 \x01(\t"\x92\x01\n\x16GenerateIdTokenRequest\x12\x37\n\x04name\x18\x01 \x01(\tB)\xe0\x41\x02\xfa\x41#\n!iam.googleapis.com/ServiceAccount\x12\x11\n\tdelegates\x18\x02 \x03(\t\x12\x15\n\x08\x61udience\x18\x03 \x01(\tB\x03\xe0\x41\x02\x12\x15\n\rinclude_email\x18\x04 \x01(\x08"(\n\x17GenerateIdTokenResponse\x12\r\n\x05token\x18\x01 \x01(\tB\xe7\x01\n#com.google.cloud.iam.credentials.v1B\x19IAMCredentialsCommonProtoP\x01ZDgoogle.golang.org/genproto/googleapis/iam/credentials/v1;credentials\xf8\x01\x01\xea\x41Y\n!iam.googleapis.com/ServiceAccount\x12\x34projects/{project}/serviceAccounts/{service_account}b\x06proto3', - dependencies=[ - google_dot_api_dot_field__behavior__pb2.DESCRIPTOR, - google_dot_api_dot_resource__pb2.DESCRIPTOR, - google_dot_protobuf_dot_duration__pb2.DESCRIPTOR, - google_dot_protobuf_dot_timestamp__pb2.DESCRIPTOR, - ], -) - - -_GENERATEACCESSTOKENREQUEST = _descriptor.Descriptor( - name="GenerateAccessTokenRequest", - full_name="google.iam.credentials.v1.GenerateAccessTokenRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name="name", - full_name="google.iam.credentials.v1.GenerateAccessTokenRequest.name", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"".decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=b"\340A\002\372A#\n!iam.googleapis.com/ServiceAccount", - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="delegates", - full_name="google.iam.credentials.v1.GenerateAccessTokenRequest.delegates", - index=1, - number=2, - type=9, - cpp_type=9, - label=3, - has_default_value=False, - default_value=[], - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="scope", - full_name="google.iam.credentials.v1.GenerateAccessTokenRequest.scope", - index=2, - number=4, - type=9, - cpp_type=9, - label=3, - has_default_value=False, - default_value=[], - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=b"\340A\002", - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="lifetime", - full_name="google.iam.credentials.v1.GenerateAccessTokenRequest.lifetime", - index=3, - number=7, - type=11, - cpp_type=10, - label=1, - has_default_value=False, - default_value=None, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=207, - serialized_end=376, -) - - -_GENERATEACCESSTOKENRESPONSE = _descriptor.Descriptor( - name="GenerateAccessTokenResponse", - full_name="google.iam.credentials.v1.GenerateAccessTokenResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name="access_token", - full_name="google.iam.credentials.v1.GenerateAccessTokenResponse.access_token", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"".decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="expire_time", - full_name="google.iam.credentials.v1.GenerateAccessTokenResponse.expire_time", - index=1, - number=3, - type=11, - cpp_type=10, - label=1, - has_default_value=False, - default_value=None, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=378, - serialized_end=478, -) - - -_SIGNBLOBREQUEST = _descriptor.Descriptor( - name="SignBlobRequest", - full_name="google.iam.credentials.v1.SignBlobRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name="name", - full_name="google.iam.credentials.v1.SignBlobRequest.name", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"".decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=b"\340A\002\372A#\n!iam.googleapis.com/ServiceAccount", - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="delegates", - full_name="google.iam.credentials.v1.SignBlobRequest.delegates", - index=1, - number=3, - type=9, - cpp_type=9, - label=3, - has_default_value=False, - default_value=[], - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="payload", - full_name="google.iam.credentials.v1.SignBlobRequest.payload", - index=2, - number=5, - type=12, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"", - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=b"\340A\002", - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=480, - serialized_end=595, -) - - -_SIGNBLOBRESPONSE = _descriptor.Descriptor( - name="SignBlobResponse", - full_name="google.iam.credentials.v1.SignBlobResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name="key_id", - full_name="google.iam.credentials.v1.SignBlobResponse.key_id", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"".decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="signed_blob", - full_name="google.iam.credentials.v1.SignBlobResponse.signed_blob", - index=1, - number=4, - type=12, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"", - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=597, - serialized_end=652, -) - - -_SIGNJWTREQUEST = _descriptor.Descriptor( - name="SignJwtRequest", - full_name="google.iam.credentials.v1.SignJwtRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name="name", - full_name="google.iam.credentials.v1.SignJwtRequest.name", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"".decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=b"\340A\002\372A#\n!iam.googleapis.com/ServiceAccount", - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="delegates", - full_name="google.iam.credentials.v1.SignJwtRequest.delegates", - index=1, - number=3, - type=9, - cpp_type=9, - label=3, - has_default_value=False, - default_value=[], - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="payload", - full_name="google.iam.credentials.v1.SignJwtRequest.payload", - index=2, - number=5, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"".decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=b"\340A\002", - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=654, - serialized_end=768, -) - - -_SIGNJWTRESPONSE = _descriptor.Descriptor( - name="SignJwtResponse", - full_name="google.iam.credentials.v1.SignJwtResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name="key_id", - full_name="google.iam.credentials.v1.SignJwtResponse.key_id", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"".decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="signed_jwt", - full_name="google.iam.credentials.v1.SignJwtResponse.signed_jwt", - index=1, - number=2, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"".decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=770, - serialized_end=823, -) - - -_GENERATEIDTOKENREQUEST = _descriptor.Descriptor( - name="GenerateIdTokenRequest", - full_name="google.iam.credentials.v1.GenerateIdTokenRequest", - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name="name", - full_name="google.iam.credentials.v1.GenerateIdTokenRequest.name", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"".decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=b"\340A\002\372A#\n!iam.googleapis.com/ServiceAccount", - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="delegates", - full_name="google.iam.credentials.v1.GenerateIdTokenRequest.delegates", - index=1, - number=2, - type=9, - cpp_type=9, - label=3, - has_default_value=False, - default_value=[], - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="audience", - full_name="google.iam.credentials.v1.GenerateIdTokenRequest.audience", - index=2, - number=3, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"".decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=b"\340A\002", - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - _descriptor.FieldDescriptor( - name="include_email", - full_name="google.iam.credentials.v1.GenerateIdTokenRequest.include_email", - index=3, - number=4, - type=8, - cpp_type=7, - label=1, - has_default_value=False, - default_value=False, - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=826, - serialized_end=972, -) - - -_GENERATEIDTOKENRESPONSE = _descriptor.Descriptor( - name="GenerateIdTokenResponse", - full_name="google.iam.credentials.v1.GenerateIdTokenResponse", - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name="token", - full_name="google.iam.credentials.v1.GenerateIdTokenResponse.token", - index=0, - number=1, - type=9, - cpp_type=9, - label=1, - has_default_value=False, - default_value=b"".decode("utf-8"), - message_type=None, - enum_type=None, - containing_type=None, - is_extension=False, - extension_scope=None, - serialized_options=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - ), - ], - extensions=[], - nested_types=[], - enum_types=[], - serialized_options=None, - is_extendable=False, - syntax="proto3", - extension_ranges=[], - oneofs=[], - serialized_start=974, - serialized_end=1014, -) - -_GENERATEACCESSTOKENREQUEST.fields_by_name[ - "lifetime" -].message_type = google_dot_protobuf_dot_duration__pb2._DURATION -_GENERATEACCESSTOKENRESPONSE.fields_by_name[ - "expire_time" -].message_type = google_dot_protobuf_dot_timestamp__pb2._TIMESTAMP -DESCRIPTOR.message_types_by_name[ - "GenerateAccessTokenRequest" -] = _GENERATEACCESSTOKENREQUEST -DESCRIPTOR.message_types_by_name[ - "GenerateAccessTokenResponse" -] = _GENERATEACCESSTOKENRESPONSE -DESCRIPTOR.message_types_by_name["SignBlobRequest"] = _SIGNBLOBREQUEST -DESCRIPTOR.message_types_by_name["SignBlobResponse"] = _SIGNBLOBRESPONSE -DESCRIPTOR.message_types_by_name["SignJwtRequest"] = _SIGNJWTREQUEST -DESCRIPTOR.message_types_by_name["SignJwtResponse"] = _SIGNJWTRESPONSE -DESCRIPTOR.message_types_by_name["GenerateIdTokenRequest"] = _GENERATEIDTOKENREQUEST -DESCRIPTOR.message_types_by_name["GenerateIdTokenResponse"] = _GENERATEIDTOKENRESPONSE -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - -GenerateAccessTokenRequest = _reflection.GeneratedProtocolMessageType( - "GenerateAccessTokenRequest", - (_message.Message,), - { - "DESCRIPTOR": _GENERATEACCESSTOKENREQUEST, - "__module__": "google.cloud.iam_credentials_v1.proto.common_pb2", - "__doc__": """ -Attributes: - name: - Required. The resource name of the service account for which - the credentials are requested, in the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. - The ``-`` wildcard character is required; replacing it with a - project ID is invalid. - delegates: - The sequence of service accounts in a delegation chain. Each - service account must be granted the - ``roles/iam.serviceAccountTokenCreator`` role on its next - service account in the chain. The last service account in the - chain must be granted the - ``roles/iam.serviceAccountTokenCreator`` role on the service - account that is specified in the ``name`` field of the - request. The delegates must have the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. - The ``-`` wildcard character is required; replacing it with a - project ID is invalid. - scope: - Required. Code to identify the scopes to be included in the - OAuth 2.0 access token. See - https://developers.google.com/identity/protocols/googlescopes - for more information. At least one value required. - lifetime: - The desired lifetime duration of the access token in seconds. - Must be set to a value less than or equal to 3600 (1 hour). If - a value is not specified, the token’s lifetime will be set to - a default value of one hour. - """, - # @@protoc_insertion_point(class_scope:google.iam.credentials.v1.GenerateAccessTokenRequest) - }, -) -_sym_db.RegisterMessage(GenerateAccessTokenRequest) - -GenerateAccessTokenResponse = _reflection.GeneratedProtocolMessageType( - "GenerateAccessTokenResponse", - (_message.Message,), - { - "DESCRIPTOR": _GENERATEACCESSTOKENRESPONSE, - "__module__": "google.cloud.iam_credentials_v1.proto.common_pb2", - "__doc__": """ -Attributes: - access_token: - The OAuth 2.0 access token. - expire_time: - Token expiration time. The expiration time is always set. - """, - # @@protoc_insertion_point(class_scope:google.iam.credentials.v1.GenerateAccessTokenResponse) - }, -) -_sym_db.RegisterMessage(GenerateAccessTokenResponse) - -SignBlobRequest = _reflection.GeneratedProtocolMessageType( - "SignBlobRequest", - (_message.Message,), - { - "DESCRIPTOR": _SIGNBLOBREQUEST, - "__module__": "google.cloud.iam_credentials_v1.proto.common_pb2", - "__doc__": """ -Attributes: - name: - Required. The resource name of the service account for which - the credentials are requested, in the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. - The ``-`` wildcard character is required; replacing it with a - project ID is invalid. - delegates: - The sequence of service accounts in a delegation chain. Each - service account must be granted the - ``roles/iam.serviceAccountTokenCreator`` role on its next - service account in the chain. The last service account in the - chain must be granted the - ``roles/iam.serviceAccountTokenCreator`` role on the service - account that is specified in the ``name`` field of the - request. The delegates must have the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. - The ``-`` wildcard character is required; replacing it with a - project ID is invalid. - payload: - Required. The bytes to sign. - """, - # @@protoc_insertion_point(class_scope:google.iam.credentials.v1.SignBlobRequest) - }, -) -_sym_db.RegisterMessage(SignBlobRequest) - -SignBlobResponse = _reflection.GeneratedProtocolMessageType( - "SignBlobResponse", - (_message.Message,), - { - "DESCRIPTOR": _SIGNBLOBRESPONSE, - "__module__": "google.cloud.iam_credentials_v1.proto.common_pb2", - "__doc__": """ -Attributes: - key_id: - The ID of the key used to sign the blob. - signed_blob: - The signed blob. - """, - # @@protoc_insertion_point(class_scope:google.iam.credentials.v1.SignBlobResponse) - }, -) -_sym_db.RegisterMessage(SignBlobResponse) - -SignJwtRequest = _reflection.GeneratedProtocolMessageType( - "SignJwtRequest", - (_message.Message,), - { - "DESCRIPTOR": _SIGNJWTREQUEST, - "__module__": "google.cloud.iam_credentials_v1.proto.common_pb2", - "__doc__": """ -Attributes: - name: - Required. The resource name of the service account for which - the credentials are requested, in the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. - The ``-`` wildcard character is required; replacing it with a - project ID is invalid. - delegates: - The sequence of service accounts in a delegation chain. Each - service account must be granted the - ``roles/iam.serviceAccountTokenCreator`` role on its next - service account in the chain. The last service account in the - chain must be granted the - ``roles/iam.serviceAccountTokenCreator`` role on the service - account that is specified in the ``name`` field of the - request. The delegates must have the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. - The ``-`` wildcard character is required; replacing it with a - project ID is invalid. - payload: - Required. The JWT payload to sign: a JSON object that contains - a JWT Claims Set. - """, - # @@protoc_insertion_point(class_scope:google.iam.credentials.v1.SignJwtRequest) - }, -) -_sym_db.RegisterMessage(SignJwtRequest) - -SignJwtResponse = _reflection.GeneratedProtocolMessageType( - "SignJwtResponse", - (_message.Message,), - { - "DESCRIPTOR": _SIGNJWTRESPONSE, - "__module__": "google.cloud.iam_credentials_v1.proto.common_pb2", - "__doc__": """ -Attributes: - key_id: - The ID of the key used to sign the JWT. - signed_jwt: - The signed JWT. - """, - # @@protoc_insertion_point(class_scope:google.iam.credentials.v1.SignJwtResponse) - }, -) -_sym_db.RegisterMessage(SignJwtResponse) - -GenerateIdTokenRequest = _reflection.GeneratedProtocolMessageType( - "GenerateIdTokenRequest", - (_message.Message,), - { - "DESCRIPTOR": _GENERATEIDTOKENREQUEST, - "__module__": "google.cloud.iam_credentials_v1.proto.common_pb2", - "__doc__": """ -Attributes: - name: - Required. The resource name of the service account for which - the credentials are requested, in the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. - The ``-`` wildcard character is required; replacing it with a - project ID is invalid. - delegates: - The sequence of service accounts in a delegation chain. Each - service account must be granted the - ``roles/iam.serviceAccountTokenCreator`` role on its next - service account in the chain. The last service account in the - chain must be granted the - ``roles/iam.serviceAccountTokenCreator`` role on the service - account that is specified in the ``name`` field of the - request. The delegates must have the following format: - ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. - The ``-`` wildcard character is required; replacing it with a - project ID is invalid. - audience: - Required. The audience for the token, such as the API or - account that this token grants access to. - include_email: - Include the service account email in the token. If set to - ``true``, the token will contain ``email`` and - ``email_verified`` claims. - """, - # @@protoc_insertion_point(class_scope:google.iam.credentials.v1.GenerateIdTokenRequest) - }, -) -_sym_db.RegisterMessage(GenerateIdTokenRequest) - -GenerateIdTokenResponse = _reflection.GeneratedProtocolMessageType( - "GenerateIdTokenResponse", - (_message.Message,), - { - "DESCRIPTOR": _GENERATEIDTOKENRESPONSE, - "__module__": "google.cloud.iam_credentials_v1.proto.common_pb2", - "__doc__": """ -Attributes: - token: - The OpenId Connect ID token. - """, - # @@protoc_insertion_point(class_scope:google.iam.credentials.v1.GenerateIdTokenResponse) - }, -) -_sym_db.RegisterMessage(GenerateIdTokenResponse) - - -DESCRIPTOR._options = None -_GENERATEACCESSTOKENREQUEST.fields_by_name["name"]._options = None -_GENERATEACCESSTOKENREQUEST.fields_by_name["scope"]._options = None -_SIGNBLOBREQUEST.fields_by_name["name"]._options = None -_SIGNBLOBREQUEST.fields_by_name["payload"]._options = None -_SIGNJWTREQUEST.fields_by_name["name"]._options = None -_SIGNJWTREQUEST.fields_by_name["payload"]._options = None -_GENERATEIDTOKENREQUEST.fields_by_name["name"]._options = None -_GENERATEIDTOKENREQUEST.fields_by_name["audience"]._options = None -# @@protoc_insertion_point(module_scope) diff --git a/google/cloud/iam_credentials_v1/proto/common_pb2_grpc.py b/google/cloud/iam_credentials_v1/proto/common_pb2_grpc.py deleted file mode 100644 index 8a93939..0000000 --- a/google/cloud/iam_credentials_v1/proto/common_pb2_grpc.py +++ /dev/null @@ -1,3 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -"""Client and server classes corresponding to protobuf-defined services.""" -import grpc diff --git a/google/cloud/iam_credentials_v1/proto/iamcredentials_pb2.py b/google/cloud/iam_credentials_v1/proto/iamcredentials_pb2.py deleted file mode 100644 index 831fecd..0000000 --- a/google/cloud/iam_credentials_v1/proto/iamcredentials_pb2.py +++ /dev/null @@ -1,98 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: google/cloud/iam_credentials_v1/proto/iamcredentials.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 -from google.api import client_pb2 as google_dot_api_dot_client__pb2 -from google.cloud.iam_credentials_v1.proto import ( - common_pb2 as google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2, -) - - -DESCRIPTOR = _descriptor.FileDescriptor( - name="google/cloud/iam_credentials_v1/proto/iamcredentials.proto", - package="google.iam.credentials.v1", - syntax="proto3", - serialized_options=b"\n#com.google.cloud.iam.credentials.v1B\023IAMCredentialsProtoP\001ZDgoogle.golang.org/genproto/googleapis/iam/credentials/v1;credentials\370\001\001", - create_key=_descriptor._internal_create_key, - serialized_pb=b'\n:google/cloud/iam_credentials_v1/proto/iamcredentials.proto\x12\x19google.iam.credentials.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x32google/cloud/iam_credentials_v1/proto/common.proto2\xad\x07\n\x0eIAMCredentials\x12\xec\x01\n\x13GenerateAccessToken\x12\x35.google.iam.credentials.v1.GenerateAccessTokenRequest\x1a\x36.google.iam.credentials.v1.GenerateAccessTokenResponse"f\x82\xd3\xe4\x93\x02@";/v1/{name=projects/*/serviceAccounts/*}:generateAccessToken:\x01*\xda\x41\x1dname,delegates,scope,lifetime\x12\xe4\x01\n\x0fGenerateIdToken\x12\x31.google.iam.credentials.v1.GenerateIdTokenRequest\x1a\x32.google.iam.credentials.v1.GenerateIdTokenResponse"j\x82\xd3\xe4\x93\x02<"7/v1/{name=projects/*/serviceAccounts/*}:generateIdToken:\x01*\xda\x41%name,delegates,audience,include_email\x12\xb9\x01\n\x08SignBlob\x12*.google.iam.credentials.v1.SignBlobRequest\x1a+.google.iam.credentials.v1.SignBlobResponse"T\x82\xd3\xe4\x93\x02\x35"0/v1/{name=projects/*/serviceAccounts/*}:signBlob:\x01*\xda\x41\x16name,delegates,payload\x12\xb5\x01\n\x07SignJwt\x12).google.iam.credentials.v1.SignJwtRequest\x1a*.google.iam.credentials.v1.SignJwtResponse"S\x82\xd3\xe4\x93\x02\x34"//v1/{name=projects/*/serviceAccounts/*}:signJwt:\x01*\xda\x41\x16name,delegates,payload\x1aQ\xca\x41\x1diamcredentials.googleapis.com\xd2\x41.https://www.googleapis.com/auth/cloud-platformB\x85\x01\n#com.google.cloud.iam.credentials.v1B\x13IAMCredentialsProtoP\x01ZDgoogle.golang.org/genproto/googleapis/iam/credentials/v1;credentials\xf8\x01\x01\x62\x06proto3', - dependencies=[ - google_dot_api_dot_annotations__pb2.DESCRIPTOR, - google_dot_api_dot_client__pb2.DESCRIPTOR, - google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.DESCRIPTOR, - ], -) - - -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - -DESCRIPTOR._options = None - -_IAMCREDENTIALS = _descriptor.ServiceDescriptor( - name="IAMCredentials", - full_name="google.iam.credentials.v1.IAMCredentials", - file=DESCRIPTOR, - index=0, - serialized_options=b"\312A\035iamcredentials.googleapis.com\322A.https://www.googleapis.com/auth/cloud-platform", - create_key=_descriptor._internal_create_key, - serialized_start=197, - serialized_end=1138, - methods=[ - _descriptor.MethodDescriptor( - name="GenerateAccessToken", - full_name="google.iam.credentials.v1.IAMCredentials.GenerateAccessToken", - index=0, - containing_service=None, - input_type=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2._GENERATEACCESSTOKENREQUEST, - output_type=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2._GENERATEACCESSTOKENRESPONSE, - serialized_options=b'\202\323\344\223\002@";/v1/{name=projects/*/serviceAccounts/*}:generateAccessToken:\001*\332A\035name,delegates,scope,lifetime', - create_key=_descriptor._internal_create_key, - ), - _descriptor.MethodDescriptor( - name="GenerateIdToken", - full_name="google.iam.credentials.v1.IAMCredentials.GenerateIdToken", - index=1, - containing_service=None, - input_type=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2._GENERATEIDTOKENREQUEST, - output_type=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2._GENERATEIDTOKENRESPONSE, - serialized_options=b'\202\323\344\223\002<"7/v1/{name=projects/*/serviceAccounts/*}:generateIdToken:\001*\332A%name,delegates,audience,include_email', - create_key=_descriptor._internal_create_key, - ), - _descriptor.MethodDescriptor( - name="SignBlob", - full_name="google.iam.credentials.v1.IAMCredentials.SignBlob", - index=2, - containing_service=None, - input_type=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2._SIGNBLOBREQUEST, - output_type=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2._SIGNBLOBRESPONSE, - serialized_options=b'\202\323\344\223\0025"0/v1/{name=projects/*/serviceAccounts/*}:signBlob:\001*\332A\026name,delegates,payload', - create_key=_descriptor._internal_create_key, - ), - _descriptor.MethodDescriptor( - name="SignJwt", - full_name="google.iam.credentials.v1.IAMCredentials.SignJwt", - index=3, - containing_service=None, - input_type=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2._SIGNJWTREQUEST, - output_type=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2._SIGNJWTRESPONSE, - serialized_options=b'\202\323\344\223\0024"//v1/{name=projects/*/serviceAccounts/*}:signJwt:\001*\332A\026name,delegates,payload', - create_key=_descriptor._internal_create_key, - ), - ], -) -_sym_db.RegisterServiceDescriptor(_IAMCREDENTIALS) - -DESCRIPTOR.services_by_name["IAMCredentials"] = _IAMCREDENTIALS - -# @@protoc_insertion_point(module_scope) diff --git a/google/cloud/iam_credentials_v1/proto/iamcredentials_pb2_grpc.py b/google/cloud/iam_credentials_v1/proto/iamcredentials_pb2_grpc.py deleted file mode 100644 index 88d615d..0000000 --- a/google/cloud/iam_credentials_v1/proto/iamcredentials_pb2_grpc.py +++ /dev/null @@ -1,239 +0,0 @@ -# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! -"""Client and server classes corresponding to protobuf-defined services.""" -import grpc - -from google.cloud.iam_credentials_v1.proto import ( - common_pb2 as google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2, -) - - -class IAMCredentialsStub(object): - """A service account is a special type of Google account that belongs to your - application or a virtual machine (VM), instead of to an individual end user. - Your application assumes the identity of the service account to call Google - APIs, so that the users aren't directly involved. - - Service account credentials are used to temporarily assume the identity - of the service account. Supported credential types include OAuth 2.0 access - tokens, OpenID Connect ID tokens, self-signed JSON Web Tokens (JWTs), and - more. - """ - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.GenerateAccessToken = channel.unary_unary( - "/google.iam.credentials.v1.IAMCredentials/GenerateAccessToken", - request_serializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.GenerateAccessTokenRequest.SerializeToString, - response_deserializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.GenerateAccessTokenResponse.FromString, - ) - self.GenerateIdToken = channel.unary_unary( - "/google.iam.credentials.v1.IAMCredentials/GenerateIdToken", - request_serializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.GenerateIdTokenRequest.SerializeToString, - response_deserializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.GenerateIdTokenResponse.FromString, - ) - self.SignBlob = channel.unary_unary( - "/google.iam.credentials.v1.IAMCredentials/SignBlob", - request_serializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.SignBlobRequest.SerializeToString, - response_deserializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.SignBlobResponse.FromString, - ) - self.SignJwt = channel.unary_unary( - "/google.iam.credentials.v1.IAMCredentials/SignJwt", - request_serializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.SignJwtRequest.SerializeToString, - response_deserializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.SignJwtResponse.FromString, - ) - - -class IAMCredentialsServicer(object): - """A service account is a special type of Google account that belongs to your - application or a virtual machine (VM), instead of to an individual end user. - Your application assumes the identity of the service account to call Google - APIs, so that the users aren't directly involved. - - Service account credentials are used to temporarily assume the identity - of the service account. Supported credential types include OAuth 2.0 access - tokens, OpenID Connect ID tokens, self-signed JSON Web Tokens (JWTs), and - more. - """ - - def GenerateAccessToken(self, request, context): - """Generates an OAuth 2.0 access token for a service account. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - def GenerateIdToken(self, request, context): - """Generates an OpenID Connect ID token for a service account. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - def SignBlob(self, request, context): - """Signs a blob using a service account's system-managed private key. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - def SignJwt(self, request, context): - """Signs a JWT using a service account's system-managed private key. - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details("Method not implemented!") - raise NotImplementedError("Method not implemented!") - - -def add_IAMCredentialsServicer_to_server(servicer, server): - rpc_method_handlers = { - "GenerateAccessToken": grpc.unary_unary_rpc_method_handler( - servicer.GenerateAccessToken, - request_deserializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.GenerateAccessTokenRequest.FromString, - response_serializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.GenerateAccessTokenResponse.SerializeToString, - ), - "GenerateIdToken": grpc.unary_unary_rpc_method_handler( - servicer.GenerateIdToken, - request_deserializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.GenerateIdTokenRequest.FromString, - response_serializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.GenerateIdTokenResponse.SerializeToString, - ), - "SignBlob": grpc.unary_unary_rpc_method_handler( - servicer.SignBlob, - request_deserializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.SignBlobRequest.FromString, - response_serializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.SignBlobResponse.SerializeToString, - ), - "SignJwt": grpc.unary_unary_rpc_method_handler( - servicer.SignJwt, - request_deserializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.SignJwtRequest.FromString, - response_serializer=google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.SignJwtResponse.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - "google.iam.credentials.v1.IAMCredentials", rpc_method_handlers - ) - server.add_generic_rpc_handlers((generic_handler,)) - - -# This class is part of an EXPERIMENTAL API. -class IAMCredentials(object): - """A service account is a special type of Google account that belongs to your - application or a virtual machine (VM), instead of to an individual end user. - Your application assumes the identity of the service account to call Google - APIs, so that the users aren't directly involved. - - Service account credentials are used to temporarily assume the identity - of the service account. Supported credential types include OAuth 2.0 access - tokens, OpenID Connect ID tokens, self-signed JSON Web Tokens (JWTs), and - more. - """ - - @staticmethod - def GenerateAccessToken( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_unary( - request, - target, - "/google.iam.credentials.v1.IAMCredentials/GenerateAccessToken", - google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.GenerateAccessTokenRequest.SerializeToString, - google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.GenerateAccessTokenResponse.FromString, - options, - channel_credentials, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - ) - - @staticmethod - def GenerateIdToken( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_unary( - request, - target, - "/google.iam.credentials.v1.IAMCredentials/GenerateIdToken", - google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.GenerateIdTokenRequest.SerializeToString, - google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.GenerateIdTokenResponse.FromString, - options, - channel_credentials, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - ) - - @staticmethod - def SignBlob( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_unary( - request, - target, - "/google.iam.credentials.v1.IAMCredentials/SignBlob", - google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.SignBlobRequest.SerializeToString, - google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.SignBlobResponse.FromString, - options, - channel_credentials, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - ) - - @staticmethod - def SignJwt( - request, - target, - options=(), - channel_credentials=None, - call_credentials=None, - compression=None, - wait_for_ready=None, - timeout=None, - metadata=None, - ): - return grpc.experimental.unary_unary( - request, - target, - "/google.iam.credentials.v1.IAMCredentials/SignJwt", - google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.SignJwtRequest.SerializeToString, - google_dot_cloud_dot_iam__credentials__v1_dot_proto_dot_common__pb2.SignJwtResponse.FromString, - options, - channel_credentials, - call_credentials, - compression, - wait_for_ready, - timeout, - metadata, - ) diff --git a/google/cloud/iam_credentials_v1/py.typed b/google/cloud/iam_credentials_v1/py.typed new file mode 100644 index 0000000..4d9bf55 --- /dev/null +++ b/google/cloud/iam_credentials_v1/py.typed @@ -0,0 +1,2 @@ +# Marker file for PEP 561. +# The google-iam-credentials package uses inline types. diff --git a/google/cloud/__init__.py b/google/cloud/iam_credentials_v1/services/__init__.py similarity index 71% rename from google/cloud/__init__.py rename to google/cloud/iam_credentials_v1/services/__init__.py index 9a1b64a..42ffdf2 100644 --- a/google/cloud/__init__.py +++ b/google/cloud/iam_credentials_v1/services/__init__.py @@ -1,24 +1,16 @@ # -*- coding: utf-8 -*- -# + # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - -try: - import pkg_resources - - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - - __path__ = pkgutil.extend_path(__path__, __name__) +# diff --git a/google/cloud/iam_credentials.py b/google/cloud/iam_credentials_v1/services/iam_credentials/__init__.py similarity index 73% rename from google/cloud/iam_credentials.py rename to google/cloud/iam_credentials_v1/services/iam_credentials/__init__.py index d3f7f63..7ac7578 100644 --- a/google/cloud/iam_credentials.py +++ b/google/cloud/iam_credentials_v1/services/iam_credentials/__init__.py @@ -1,27 +1,24 @@ # -*- coding: utf-8 -*- -# + # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# - -from __future__ import absolute_import - -from google.cloud.iam_credentials_v1 import IAMCredentialsClient -from google.cloud.iam_credentials_v1 import types - +from .client import IAMCredentialsClient +from .async_client import IAMCredentialsAsyncClient __all__ = ( - "types", "IAMCredentialsClient", + "IAMCredentialsAsyncClient", ) diff --git a/google/cloud/iam_credentials_v1/services/iam_credentials/async_client.py b/google/cloud/iam_credentials_v1/services/iam_credentials/async_client.py new file mode 100644 index 0000000..c113522 --- /dev/null +++ b/google/cloud/iam_credentials_v1/services/iam_credentials/async_client.py @@ -0,0 +1,583 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from collections import OrderedDict +import functools +import re +from typing import Dict, Sequence, Tuple, Type, Union +import pkg_resources + +import google.api_core.client_options as ClientOptions # type: ignore +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 +from google.auth import credentials # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.cloud.iam_credentials_v1.types import common +from google.protobuf import duration_pb2 as duration # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + +from .transports.base import IAMCredentialsTransport +from .transports.grpc_asyncio import IAMCredentialsGrpcAsyncIOTransport +from .client import IAMCredentialsClient + + +class IAMCredentialsAsyncClient: + """A service account is a special type of Google account that + belongs to your application or a virtual machine (VM), instead + of to an individual end user. Your application assumes the + identity of the service account to call Google APIs, so that the + users aren't directly involved. + + Service account credentials are used to temporarily assume the + identity of the service account. Supported credential types + include OAuth 2.0 access tokens, OpenID Connect ID tokens, self- + signed JSON Web Tokens (JWTs), and more. + """ + + _client: IAMCredentialsClient + + DEFAULT_ENDPOINT = IAMCredentialsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = IAMCredentialsClient.DEFAULT_MTLS_ENDPOINT + + from_service_account_file = IAMCredentialsClient.from_service_account_file + from_service_account_json = from_service_account_file + + get_transport_class = functools.partial( + type(IAMCredentialsClient).get_transport_class, type(IAMCredentialsClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, IAMCredentialsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + ) -> None: + """Instantiate the iam credentials client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, ~.IAMCredentialsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (ClientOptions): Custom options for the client. It + won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint, this is the default value for + the environment variable) and "auto" (auto switch to the default + mTLS endpoint if client SSL credentials is present). However, + the ``api_endpoint`` property takes precedence if provided. + (2) The ``client_cert_source`` property is used to provide client + SSL credentials for mutual TLS transport. If not provided, the + default SSL credentials will be used if present. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + """ + + self._client = IAMCredentialsClient( + credentials=credentials, transport=transport, client_options=client_options, + ) + + async def generate_access_token( + self, + request: common.GenerateAccessTokenRequest = None, + *, + name: str = None, + delegates: Sequence[str] = None, + scope: Sequence[str] = None, + lifetime: duration.Duration = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> common.GenerateAccessTokenResponse: + r"""Generates an OAuth 2.0 access token for a service + account. + + Args: + request (:class:`~.common.GenerateAccessTokenRequest`): + The request object. + name (:class:`str`): + Required. The resource name of the service account for + which the credentials are requested, in the following + format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + delegates (:class:`Sequence[str]`): + The sequence of service accounts in a delegation chain. + Each service account must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on its + next service account in the chain. The last service + account in the chain must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on the + service account that is specified in the ``name`` field + of the request. + + The delegates must have the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``delegates`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + scope (:class:`Sequence[str]`): + Required. Code to identify the scopes + to be included in the OAuth 2.0 access + token. See + https://developers.google.com/identity/protocols/googlescopes + for more information. + At least one value required. + This corresponds to the ``scope`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + lifetime (:class:`~.duration.Duration`): + The desired lifetime duration of the + access token in seconds. Must be set to + a value less than or equal to 3600 (1 + hour). If a value is not specified, the + token's lifetime will be set to a + default value of one hour. + This corresponds to the ``lifetime`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.common.GenerateAccessTokenResponse: + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + if request is not None and any([name, delegates, scope, lifetime]): + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = common.GenerateAccessTokenRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if name is not None: + request.name = name + if delegates is not None: + request.delegates = delegates + if scope is not None: + request.scope = scope + if lifetime is not None: + request.lifetime = lifetime + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.generate_access_token, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + default_timeout=60.0, + client_info=_client_info, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def generate_id_token( + self, + request: common.GenerateIdTokenRequest = None, + *, + name: str = None, + delegates: Sequence[str] = None, + audience: str = None, + include_email: bool = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> common.GenerateIdTokenResponse: + r"""Generates an OpenID Connect ID token for a service + account. + + Args: + request (:class:`~.common.GenerateIdTokenRequest`): + The request object. + name (:class:`str`): + Required. The resource name of the service account for + which the credentials are requested, in the following + format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + delegates (:class:`Sequence[str]`): + The sequence of service accounts in a delegation chain. + Each service account must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on its + next service account in the chain. The last service + account in the chain must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on the + service account that is specified in the ``name`` field + of the request. + + The delegates must have the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``delegates`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + audience (:class:`str`): + Required. The audience for the token, + such as the API or account that this + token grants access to. + This corresponds to the ``audience`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + include_email (:class:`bool`): + Include the service account email in the token. If set + to ``true``, the token will contain ``email`` and + ``email_verified`` claims. + This corresponds to the ``include_email`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.common.GenerateIdTokenResponse: + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + if request is not None and any([name, delegates, audience, include_email]): + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = common.GenerateIdTokenRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if name is not None: + request.name = name + if delegates is not None: + request.delegates = delegates + if audience is not None: + request.audience = audience + if include_email is not None: + request.include_email = include_email + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.generate_id_token, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + default_timeout=60.0, + client_info=_client_info, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def sign_blob( + self, + request: common.SignBlobRequest = None, + *, + name: str = None, + delegates: Sequence[str] = None, + payload: bytes = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> common.SignBlobResponse: + r"""Signs a blob using a service account's system-managed + private key. + + Args: + request (:class:`~.common.SignBlobRequest`): + The request object. + name (:class:`str`): + Required. The resource name of the service account for + which the credentials are requested, in the following + format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + delegates (:class:`Sequence[str]`): + The sequence of service accounts in a delegation chain. + Each service account must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on its + next service account in the chain. The last service + account in the chain must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on the + service account that is specified in the ``name`` field + of the request. + + The delegates must have the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``delegates`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + payload (:class:`bytes`): + Required. The bytes to sign. + This corresponds to the ``payload`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.common.SignBlobResponse: + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + if request is not None and any([name, delegates, payload]): + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = common.SignBlobRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if name is not None: + request.name = name + if delegates is not None: + request.delegates = delegates + if payload is not None: + request.payload = payload + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.sign_blob, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + default_timeout=60.0, + client_info=_client_info, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def sign_jwt( + self, + request: common.SignJwtRequest = None, + *, + name: str = None, + delegates: Sequence[str] = None, + payload: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> common.SignJwtResponse: + r"""Signs a JWT using a service account's system-managed + private key. + + Args: + request (:class:`~.common.SignJwtRequest`): + The request object. + name (:class:`str`): + Required. The resource name of the service account for + which the credentials are requested, in the following + format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + delegates (:class:`Sequence[str]`): + The sequence of service accounts in a delegation chain. + Each service account must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on its + next service account in the chain. The last service + account in the chain must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on the + service account that is specified in the ``name`` field + of the request. + + The delegates must have the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``delegates`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + payload (:class:`str`): + Required. The JWT payload to sign: a + JSON object that contains a JWT Claims + Set. + This corresponds to the ``payload`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.common.SignJwtResponse: + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + if request is not None and any([name, delegates, payload]): + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = common.SignJwtRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if name is not None: + request.name = name + if delegates is not None: + request.delegates = delegates + if payload is not None: + request.payload = payload + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.sign_jwt, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + default_timeout=60.0, + client_info=_client_info, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + _client_info = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution("google-cloud-iam",).version, + ) +except pkg_resources.DistributionNotFound: + _client_info = gapic_v1.client_info.ClientInfo() + + +__all__ = ("IAMCredentialsAsyncClient",) diff --git a/google/cloud/iam_credentials_v1/services/iam_credentials/client.py b/google/cloud/iam_credentials_v1/services/iam_credentials/client.py new file mode 100644 index 0000000..169eac8 --- /dev/null +++ b/google/cloud/iam_credentials_v1/services/iam_credentials/client.py @@ -0,0 +1,709 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from collections import OrderedDict +import os +import re +from typing import Callable, Dict, Sequence, Tuple, Type, Union +import pkg_resources + +import google.api_core.client_options as ClientOptions # type: ignore +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 +from google.auth import credentials # type: ignore +from google.auth.transport import mtls # type: ignore +from google.auth.exceptions import MutualTLSChannelError # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.cloud.iam_credentials_v1.types import common +from google.protobuf import duration_pb2 as duration # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + +from .transports.base import IAMCredentialsTransport +from .transports.grpc import IAMCredentialsGrpcTransport +from .transports.grpc_asyncio import IAMCredentialsGrpcAsyncIOTransport + + +class IAMCredentialsClientMeta(type): + """Metaclass for the IAMCredentials client. + + This provides class-level methods for building and retrieving + support objects (e.g. transport) without polluting the client instance + objects. + """ + + _transport_registry = ( + OrderedDict() + ) # type: Dict[str, Type[IAMCredentialsTransport]] + _transport_registry["grpc"] = IAMCredentialsGrpcTransport + _transport_registry["grpc_asyncio"] = IAMCredentialsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[IAMCredentialsTransport]: + """Return an appropriate transport class. + + Args: + label: The name of the desired transport. If none is + provided, then the first transport in the registry is used. + + Returns: + The transport class to use. + """ + # If a specific transport is requested, return that one. + if label: + return cls._transport_registry[label] + + # No transport is requested; return the default (that is, the first one + # in the dictionary). + return next(iter(cls._transport_registry.values())) + + +class IAMCredentialsClient(metaclass=IAMCredentialsClientMeta): + """A service account is a special type of Google account that + belongs to your application or a virtual machine (VM), instead + of to an individual end user. Your application assumes the + identity of the service account to call Google APIs, so that the + users aren't directly involved. + + Service account credentials are used to temporarily assume the + identity of the service account. Supported credential types + include OAuth 2.0 access tokens, OpenID Connect ID tokens, self- + signed JSON Web Tokens (JWTs), and more. + """ + + @staticmethod + def _get_default_mtls_endpoint(api_endpoint): + """Convert api endpoint to mTLS endpoint. + Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to + "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively. + Args: + api_endpoint (Optional[str]): the api endpoint to convert. + Returns: + str: converted mTLS api endpoint. + """ + if not api_endpoint: + return api_endpoint + + mtls_endpoint_re = re.compile( + r"(?P[^.]+)(?P\.mtls)?(?P\.sandbox)?(?P\.googleapis\.com)?" + ) + + m = mtls_endpoint_re.match(api_endpoint) + name, mtls, sandbox, googledomain = m.groups() + if mtls or not googledomain: + return api_endpoint + + if sandbox: + return api_endpoint.replace( + "sandbox.googleapis.com", "mtls.sandbox.googleapis.com" + ) + + return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + + DEFAULT_ENDPOINT = "iamcredentials.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + {@api.name}: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_file(filename) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + from_service_account_json = from_service_account_file + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, IAMCredentialsTransport] = None, + client_options: ClientOptions = None, + ) -> None: + """Instantiate the iam credentials client. + + Args: + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + transport (Union[str, ~.IAMCredentialsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (ClientOptions): Custom options for the client. It + won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint, this is the default value for + the environment variable) and "auto" (auto switch to the default + mTLS endpoint if client SSL credentials is present). However, + the ``api_endpoint`` property takes precedence if provided. + (2) The ``client_cert_source`` property is used to provide client + SSL credentials for mutual TLS transport. If not provided, the + default SSL credentials will be used if present. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = ClientOptions.from_dict(client_options) + if client_options is None: + client_options = ClientOptions.ClientOptions() + + if client_options.api_endpoint is None: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never") + if use_mtls_env == "never": + client_options.api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + has_client_cert_source = ( + client_options.client_cert_source is not None + or mtls.has_default_client_cert_source() + ) + client_options.api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT + if has_client_cert_source + else self.DEFAULT_ENDPOINT + ) + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, IAMCredentialsTransport): + # transport is a IAMCredentialsTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, " + "provide its scopes directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=client_options.api_endpoint, + scopes=client_options.scopes, + api_mtls_endpoint=client_options.api_endpoint, + client_cert_source=client_options.client_cert_source, + quota_project_id=client_options.quota_project_id, + ) + + def generate_access_token( + self, + request: common.GenerateAccessTokenRequest = None, + *, + name: str = None, + delegates: Sequence[str] = None, + scope: Sequence[str] = None, + lifetime: duration.Duration = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> common.GenerateAccessTokenResponse: + r"""Generates an OAuth 2.0 access token for a service + account. + + Args: + request (:class:`~.common.GenerateAccessTokenRequest`): + The request object. + name (:class:`str`): + Required. The resource name of the service account for + which the credentials are requested, in the following + format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + delegates (:class:`Sequence[str]`): + The sequence of service accounts in a delegation chain. + Each service account must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on its + next service account in the chain. The last service + account in the chain must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on the + service account that is specified in the ``name`` field + of the request. + + The delegates must have the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``delegates`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + scope (:class:`Sequence[str]`): + Required. Code to identify the scopes + to be included in the OAuth 2.0 access + token. See + https://developers.google.com/identity/protocols/googlescopes + for more information. + At least one value required. + This corresponds to the ``scope`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + lifetime (:class:`~.duration.Duration`): + The desired lifetime duration of the + access token in seconds. Must be set to + a value less than or equal to 3600 (1 + hour). If a value is not specified, the + token's lifetime will be set to a + default value of one hour. + This corresponds to the ``lifetime`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.common.GenerateAccessTokenResponse: + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + if request is not None and any([name, delegates, scope, lifetime]): + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = common.GenerateAccessTokenRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if name is not None: + request.name = name + if delegates is not None: + request.delegates = delegates + if scope is not None: + request.scope = scope + if lifetime is not None: + request.lifetime = lifetime + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method.wrap_method( + self._transport.generate_access_token, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + default_timeout=60.0, + client_info=_client_info, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def generate_id_token( + self, + request: common.GenerateIdTokenRequest = None, + *, + name: str = None, + delegates: Sequence[str] = None, + audience: str = None, + include_email: bool = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> common.GenerateIdTokenResponse: + r"""Generates an OpenID Connect ID token for a service + account. + + Args: + request (:class:`~.common.GenerateIdTokenRequest`): + The request object. + name (:class:`str`): + Required. The resource name of the service account for + which the credentials are requested, in the following + format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + delegates (:class:`Sequence[str]`): + The sequence of service accounts in a delegation chain. + Each service account must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on its + next service account in the chain. The last service + account in the chain must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on the + service account that is specified in the ``name`` field + of the request. + + The delegates must have the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``delegates`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + audience (:class:`str`): + Required. The audience for the token, + such as the API or account that this + token grants access to. + This corresponds to the ``audience`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + include_email (:class:`bool`): + Include the service account email in the token. If set + to ``true``, the token will contain ``email`` and + ``email_verified`` claims. + This corresponds to the ``include_email`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.common.GenerateIdTokenResponse: + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + if request is not None and any([name, delegates, audience, include_email]): + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = common.GenerateIdTokenRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if name is not None: + request.name = name + if delegates is not None: + request.delegates = delegates + if audience is not None: + request.audience = audience + if include_email is not None: + request.include_email = include_email + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method.wrap_method( + self._transport.generate_id_token, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + default_timeout=60.0, + client_info=_client_info, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def sign_blob( + self, + request: common.SignBlobRequest = None, + *, + name: str = None, + delegates: Sequence[str] = None, + payload: bytes = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> common.SignBlobResponse: + r"""Signs a blob using a service account's system-managed + private key. + + Args: + request (:class:`~.common.SignBlobRequest`): + The request object. + name (:class:`str`): + Required. The resource name of the service account for + which the credentials are requested, in the following + format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + delegates (:class:`Sequence[str]`): + The sequence of service accounts in a delegation chain. + Each service account must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on its + next service account in the chain. The last service + account in the chain must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on the + service account that is specified in the ``name`` field + of the request. + + The delegates must have the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``delegates`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + payload (:class:`bytes`): + Required. The bytes to sign. + This corresponds to the ``payload`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.common.SignBlobResponse: + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + if request is not None and any([name, delegates, payload]): + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = common.SignBlobRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if name is not None: + request.name = name + if delegates is not None: + request.delegates = delegates + if payload is not None: + request.payload = payload + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method.wrap_method( + self._transport.sign_blob, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + default_timeout=60.0, + client_info=_client_info, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def sign_jwt( + self, + request: common.SignJwtRequest = None, + *, + name: str = None, + delegates: Sequence[str] = None, + payload: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> common.SignJwtResponse: + r"""Signs a JWT using a service account's system-managed + private key. + + Args: + request (:class:`~.common.SignJwtRequest`): + The request object. + name (:class:`str`): + Required. The resource name of the service account for + which the credentials are requested, in the following + format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + delegates (:class:`Sequence[str]`): + The sequence of service accounts in a delegation chain. + Each service account must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on its + next service account in the chain. The last service + account in the chain must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on the + service account that is specified in the ``name`` field + of the request. + + The delegates must have the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it + with a project ID is invalid. + This corresponds to the ``delegates`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + payload (:class:`str`): + Required. The JWT payload to sign: a + JSON object that contains a JWT Claims + Set. + This corresponds to the ``payload`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.common.SignJwtResponse: + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + if request is not None and any([name, delegates, payload]): + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = common.SignJwtRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if name is not None: + request.name = name + if delegates is not None: + request.delegates = delegates + if payload is not None: + request.payload = payload + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method.wrap_method( + self._transport.sign_jwt, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + default_timeout=60.0, + client_info=_client_info, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + _client_info = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution("google-cloud-iam",).version, + ) +except pkg_resources.DistributionNotFound: + _client_info = gapic_v1.client_info.ClientInfo() + + +__all__ = ("IAMCredentialsClient",) diff --git a/google/cloud/iam_credentials_v1/services/iam_credentials/transports/__init__.py b/google/cloud/iam_credentials_v1/services/iam_credentials/transports/__init__.py new file mode 100644 index 0000000..53573d0 --- /dev/null +++ b/google/cloud/iam_credentials_v1/services/iam_credentials/transports/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from collections import OrderedDict +from typing import Dict, Type + +from .base import IAMCredentialsTransport +from .grpc import IAMCredentialsGrpcTransport +from .grpc_asyncio import IAMCredentialsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[IAMCredentialsTransport]] +_transport_registry["grpc"] = IAMCredentialsGrpcTransport +_transport_registry["grpc_asyncio"] = IAMCredentialsGrpcAsyncIOTransport + + +__all__ = ( + "IAMCredentialsTransport", + "IAMCredentialsGrpcTransport", + "IAMCredentialsGrpcAsyncIOTransport", +) diff --git a/google/cloud/iam_credentials_v1/services/iam_credentials/transports/base.py b/google/cloud/iam_credentials_v1/services/iam_credentials/transports/base.py new file mode 100644 index 0000000..ddeacc9 --- /dev/null +++ b/google/cloud/iam_credentials_v1/services/iam_credentials/transports/base.py @@ -0,0 +1,129 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import abc +import typing + +from google import auth +from google.api_core import exceptions # type: ignore +from google.auth import credentials # type: ignore + +from google.cloud.iam_credentials_v1.types import common + + +class IAMCredentialsTransport(abc.ABC): + """Abstract transport class for IAMCredentials.""" + + AUTH_SCOPES = ("https://www.googleapis.com/auth/cloud-platform",) + + def __init__( + self, + *, + host: str = "iamcredentials.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: typing.Optional[str] = None, + scopes: typing.Optional[typing.Sequence[str]] = AUTH_SCOPES, + quota_project_id: typing.Optional[str] = None, + **kwargs, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + 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. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + """ + # Save the hostname. Default to port 443 (HTTPS) if none is specified. + if ":" not in host: + host += ":443" + self._host = host + + # If no credentials are provided, then determine the appropriate + # defaults. + if credentials and credentials_file: + raise exceptions.DuplicateCredentialArgs( + "'credentials_file' and 'credentials' are mutually exclusive" + ) + + if credentials_file is not None: + credentials, _ = auth.load_credentials_from_file( + credentials_file, scopes=scopes, quota_project_id=quota_project_id + ) + + elif credentials is None: + credentials, _ = auth.default( + scopes=scopes, quota_project_id=quota_project_id + ) + + # Save the credentials. + self._credentials = credentials + + @property + def generate_access_token( + self, + ) -> typing.Callable[ + [common.GenerateAccessTokenRequest], + typing.Union[ + common.GenerateAccessTokenResponse, + typing.Awaitable[common.GenerateAccessTokenResponse], + ], + ]: + raise NotImplementedError() + + @property + def generate_id_token( + self, + ) -> typing.Callable[ + [common.GenerateIdTokenRequest], + typing.Union[ + common.GenerateIdTokenResponse, + typing.Awaitable[common.GenerateIdTokenResponse], + ], + ]: + raise NotImplementedError() + + @property + def sign_blob( + self, + ) -> typing.Callable[ + [common.SignBlobRequest], + typing.Union[ + common.SignBlobResponse, typing.Awaitable[common.SignBlobResponse] + ], + ]: + raise NotImplementedError() + + @property + def sign_jwt( + self, + ) -> typing.Callable[ + [common.SignJwtRequest], + typing.Union[common.SignJwtResponse, typing.Awaitable[common.SignJwtResponse]], + ]: + raise NotImplementedError() + + +__all__ = ("IAMCredentialsTransport",) diff --git a/google/cloud/iam_credentials_v1/services/iam_credentials/transports/grpc.py b/google/cloud/iam_credentials_v1/services/iam_credentials/transports/grpc.py new file mode 100644 index 0000000..4efc50b --- /dev/null +++ b/google/cloud/iam_credentials_v1/services/iam_credentials/transports/grpc.py @@ -0,0 +1,322 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from typing import Callable, Dict, Optional, Sequence, Tuple + +from google.api_core import grpc_helpers # type: ignore +from google import auth # type: ignore +from google.auth import credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + + +import grpc # type: ignore + +from google.cloud.iam_credentials_v1.types import common + +from .base import IAMCredentialsTransport + + +class IAMCredentialsGrpcTransport(IAMCredentialsTransport): + """gRPC backend transport for IAMCredentials. + + A service account is a special type of Google account that + belongs to your application or a virtual machine (VM), instead + of to an individual end user. Your application assumes the + identity of the service account to call Google APIs, so that the + users aren't directly involved. + + Service account credentials are used to temporarily assume the + identity of the service account. Supported credential types + include OAuth 2.0 access tokens, OpenID Connect ID tokens, self- + signed JSON Web Tokens (JWTs), and more. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _stubs: Dict[str, Callable] + + def __init__( + self, + *, + host: str = "iamcredentials.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: str = None, + scopes: Sequence[str] = None, + channel: grpc.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If + provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): A + callback to provide client SSL certificate bytes and private key + bytes, both in PEM format. It is ignored if ``api_mtls_endpoint`` + is None. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + self._stubs = {} # type: Dict[str, Callable] + + @classmethod + def create_channel( + cls, + host: str = "iamcredentials.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: str = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs + ) -> grpc.Channel: + """Create and return a gRPC channel object. + Args: + address (Optionsl[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + 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. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + 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, + **kwargs + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Sanity check: Only create a new channel if we do not already + # have one. + if not hasattr(self, "_grpc_channel"): + self._grpc_channel = self.create_channel( + self._host, credentials=self._credentials, + ) + + # Return the channel from cache. + return self._grpc_channel + + @property + def generate_access_token( + self, + ) -> Callable[ + [common.GenerateAccessTokenRequest], common.GenerateAccessTokenResponse + ]: + r"""Return a callable for the generate access token method over gRPC. + + Generates an OAuth 2.0 access token for a service + account. + + Returns: + Callable[[~.GenerateAccessTokenRequest], + ~.GenerateAccessTokenResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "generate_access_token" not in self._stubs: + self._stubs["generate_access_token"] = self.grpc_channel.unary_unary( + "/google.iam.credentials.v1.IAMCredentials/GenerateAccessToken", + request_serializer=common.GenerateAccessTokenRequest.serialize, + response_deserializer=common.GenerateAccessTokenResponse.deserialize, + ) + return self._stubs["generate_access_token"] + + @property + def generate_id_token( + self, + ) -> Callable[[common.GenerateIdTokenRequest], common.GenerateIdTokenResponse]: + r"""Return a callable for the generate id token method over gRPC. + + Generates an OpenID Connect ID token for a service + account. + + Returns: + Callable[[~.GenerateIdTokenRequest], + ~.GenerateIdTokenResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "generate_id_token" not in self._stubs: + self._stubs["generate_id_token"] = self.grpc_channel.unary_unary( + "/google.iam.credentials.v1.IAMCredentials/GenerateIdToken", + request_serializer=common.GenerateIdTokenRequest.serialize, + response_deserializer=common.GenerateIdTokenResponse.deserialize, + ) + return self._stubs["generate_id_token"] + + @property + def sign_blob(self) -> Callable[[common.SignBlobRequest], common.SignBlobResponse]: + r"""Return a callable for the sign blob method over gRPC. + + Signs a blob using a service account's system-managed + private key. + + Returns: + Callable[[~.SignBlobRequest], + ~.SignBlobResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "sign_blob" not in self._stubs: + self._stubs["sign_blob"] = self.grpc_channel.unary_unary( + "/google.iam.credentials.v1.IAMCredentials/SignBlob", + request_serializer=common.SignBlobRequest.serialize, + response_deserializer=common.SignBlobResponse.deserialize, + ) + return self._stubs["sign_blob"] + + @property + def sign_jwt(self) -> Callable[[common.SignJwtRequest], common.SignJwtResponse]: + r"""Return a callable for the sign jwt method over gRPC. + + Signs a JWT using a service account's system-managed + private key. + + Returns: + Callable[[~.SignJwtRequest], + ~.SignJwtResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "sign_jwt" not in self._stubs: + self._stubs["sign_jwt"] = self.grpc_channel.unary_unary( + "/google.iam.credentials.v1.IAMCredentials/SignJwt", + request_serializer=common.SignJwtRequest.serialize, + response_deserializer=common.SignJwtResponse.deserialize, + ) + return self._stubs["sign_jwt"] + + +__all__ = ("IAMCredentialsGrpcTransport",) diff --git a/google/cloud/iam_credentials_v1/services/iam_credentials/transports/grpc_asyncio.py b/google/cloud/iam_credentials_v1/services/iam_credentials/transports/grpc_asyncio.py new file mode 100644 index 0000000..2a79974 --- /dev/null +++ b/google/cloud/iam_credentials_v1/services/iam_credentials/transports/grpc_asyncio.py @@ -0,0 +1,322 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple + +from google.api_core import grpc_helpers_async # type: ignore +from google.auth import credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + +import grpc # type: ignore +from grpc.experimental import aio # type: ignore + +from google.cloud.iam_credentials_v1.types import common + +from .base import IAMCredentialsTransport +from .grpc import IAMCredentialsGrpcTransport + + +class IAMCredentialsGrpcAsyncIOTransport(IAMCredentialsTransport): + """gRPC AsyncIO backend transport for IAMCredentials. + + A service account is a special type of Google account that + belongs to your application or a virtual machine (VM), instead + of to an individual end user. Your application assumes the + identity of the service account to call Google APIs, so that the + users aren't directly involved. + + Service account credentials are used to temporarily assume the + identity of the service account. Supported credential types + include OAuth 2.0 access tokens, OpenID Connect ID tokens, self- + signed JSON Web Tokens (JWTs), and more. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends protocol buffers over the wire using gRPC (which is built on + top of HTTP/2); the ``grpcio`` package must be installed. + """ + + _grpc_channel: aio.Channel + _stubs: Dict[str, Callable] = {} + + @classmethod + def create_channel( + cls, + host: str = "iamcredentials.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + quota_project_id: Optional[str] = None, + **kwargs, + ) -> aio.Channel: + """Create and return a gRPC AsyncIO channel object. + Args: + address (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + aio.Channel: A gRPC AsyncIO channel object. + """ + 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, + **kwargs, + ) + + def __init__( + self, + *, + host: str = "iamcredentials.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: Optional[str] = None, + scopes: Optional[Sequence[str]] = None, + channel: aio.Channel = None, + api_mtls_endpoint: str = None, + client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If + provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): A + callback to provide client SSL certificate bytes and private key + bytes, both in PEM format. It is ignored if ``api_mtls_endpoint`` + is None. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + self._stubs = {} + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Sanity check: Only create a new channel if we do not already + # have one. + if not hasattr(self, "_grpc_channel"): + self._grpc_channel = self.create_channel( + self._host, credentials=self._credentials, + ) + + # Return the channel from cache. + return self._grpc_channel + + @property + def generate_access_token( + self, + ) -> Callable[ + [common.GenerateAccessTokenRequest], + Awaitable[common.GenerateAccessTokenResponse], + ]: + r"""Return a callable for the generate access token method over gRPC. + + Generates an OAuth 2.0 access token for a service + account. + + Returns: + Callable[[~.GenerateAccessTokenRequest], + Awaitable[~.GenerateAccessTokenResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "generate_access_token" not in self._stubs: + self._stubs["generate_access_token"] = self.grpc_channel.unary_unary( + "/google.iam.credentials.v1.IAMCredentials/GenerateAccessToken", + request_serializer=common.GenerateAccessTokenRequest.serialize, + response_deserializer=common.GenerateAccessTokenResponse.deserialize, + ) + return self._stubs["generate_access_token"] + + @property + def generate_id_token( + self, + ) -> Callable[ + [common.GenerateIdTokenRequest], Awaitable[common.GenerateIdTokenResponse] + ]: + r"""Return a callable for the generate id token method over gRPC. + + Generates an OpenID Connect ID token for a service + account. + + Returns: + Callable[[~.GenerateIdTokenRequest], + Awaitable[~.GenerateIdTokenResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "generate_id_token" not in self._stubs: + self._stubs["generate_id_token"] = self.grpc_channel.unary_unary( + "/google.iam.credentials.v1.IAMCredentials/GenerateIdToken", + request_serializer=common.GenerateIdTokenRequest.serialize, + response_deserializer=common.GenerateIdTokenResponse.deserialize, + ) + return self._stubs["generate_id_token"] + + @property + def sign_blob( + self, + ) -> Callable[[common.SignBlobRequest], Awaitable[common.SignBlobResponse]]: + r"""Return a callable for the sign blob method over gRPC. + + Signs a blob using a service account's system-managed + private key. + + Returns: + Callable[[~.SignBlobRequest], + Awaitable[~.SignBlobResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "sign_blob" not in self._stubs: + self._stubs["sign_blob"] = self.grpc_channel.unary_unary( + "/google.iam.credentials.v1.IAMCredentials/SignBlob", + request_serializer=common.SignBlobRequest.serialize, + response_deserializer=common.SignBlobResponse.deserialize, + ) + return self._stubs["sign_blob"] + + @property + def sign_jwt( + self, + ) -> Callable[[common.SignJwtRequest], Awaitable[common.SignJwtResponse]]: + r"""Return a callable for the sign jwt method over gRPC. + + Signs a JWT using a service account's system-managed + private key. + + Returns: + Callable[[~.SignJwtRequest], + Awaitable[~.SignJwtResponse]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "sign_jwt" not in self._stubs: + self._stubs["sign_jwt"] = self.grpc_channel.unary_unary( + "/google.iam.credentials.v1.IAMCredentials/SignJwt", + request_serializer=common.SignJwtRequest.serialize, + response_deserializer=common.SignJwtResponse.deserialize, + ) + return self._stubs["sign_jwt"] + + +__all__ = ("IAMCredentialsGrpcAsyncIOTransport",) diff --git a/google/cloud/iam_credentials_v1/types.py b/google/cloud/iam_credentials_v1/types.py deleted file mode 100644 index ea51aaf..0000000 --- a/google/cloud/iam_credentials_v1/types.py +++ /dev/null @@ -1,50 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - - -from __future__ import absolute_import -import sys - -from google.api_core.protobuf_helpers import get_messages - -from google.cloud.iam_credentials_v1.proto import common_pb2 -from google.protobuf import duration_pb2 -from google.protobuf import timestamp_pb2 - - -_shared_modules = [ - duration_pb2, - timestamp_pb2, -] - -_local_modules = [ - common_pb2, -] - -names = [] - -for module in _shared_modules: # pragma: NO COVER - for name, message in get_messages(module).items(): - setattr(sys.modules[__name__], name, message) - names.append(name) -for module in _local_modules: - for name, message in get_messages(module).items(): - message.__module__ = "google.cloud.iam_credentials_v1.types" - setattr(sys.modules[__name__], name, message) - names.append(name) - - -__all__ = tuple(sorted(names)) diff --git a/google/cloud/iam_credentials_v1/types/__init__.py b/google/cloud/iam_credentials_v1/types/__init__.py new file mode 100644 index 0000000..ea89f4d --- /dev/null +++ b/google/cloud/iam_credentials_v1/types/__init__.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from .common import ( + GenerateAccessTokenRequest, + GenerateAccessTokenResponse, + SignBlobRequest, + SignBlobResponse, + SignJwtRequest, + SignJwtResponse, + GenerateIdTokenRequest, + GenerateIdTokenResponse, +) + + +__all__ = ( + "GenerateAccessTokenRequest", + "GenerateAccessTokenResponse", + "SignBlobRequest", + "SignBlobResponse", + "SignJwtRequest", + "SignJwtResponse", + "GenerateIdTokenRequest", + "GenerateIdTokenResponse", +) diff --git a/google/cloud/iam_credentials_v1/types/common.py b/google/cloud/iam_credentials_v1/types/common.py new file mode 100644 index 0000000..5da8235 --- /dev/null +++ b/google/cloud/iam_credentials_v1/types/common.py @@ -0,0 +1,258 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import proto # type: ignore + + +from google.protobuf import duration_pb2 as duration # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + + +__protobuf__ = proto.module( + package="google.iam.credentials.v1", + manifest={ + "GenerateAccessTokenRequest", + "GenerateAccessTokenResponse", + "SignBlobRequest", + "SignBlobResponse", + "SignJwtRequest", + "SignJwtResponse", + "GenerateIdTokenRequest", + "GenerateIdTokenResponse", + }, +) + + +class GenerateAccessTokenRequest(proto.Message): + r""" + + Attributes: + name (str): + Required. The resource name of the service account for which + the credentials are requested, in the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it with + a project ID is invalid. + delegates (Sequence[str]): + The sequence of service accounts in a delegation chain. Each + service account must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on its next + service account in the chain. The last service account in + the chain must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on the service + account that is specified in the ``name`` field of the + request. + + The delegates must have the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it with + a project ID is invalid. + scope (Sequence[str]): + Required. Code to identify the scopes to be + included in the OAuth 2.0 access token. See + https://developers.google.com/identity/protocols/googlescopes + for more information. + At least one value required. + lifetime (~.duration.Duration): + The desired lifetime duration of the access + token in seconds. Must be set to a value less + than or equal to 3600 (1 hour). If a value is + not specified, the token's lifetime will be set + to a default value of one hour. + """ + + name = proto.Field(proto.STRING, number=1) + + delegates = proto.RepeatedField(proto.STRING, number=2) + + scope = proto.RepeatedField(proto.STRING, number=4) + + lifetime = proto.Field(proto.MESSAGE, number=7, message=duration.Duration,) + + +class GenerateAccessTokenResponse(proto.Message): + r""" + + Attributes: + access_token (str): + The OAuth 2.0 access token. + expire_time (~.timestamp.Timestamp): + Token expiration time. + The expiration time is always set. + """ + + access_token = proto.Field(proto.STRING, number=1) + + expire_time = proto.Field(proto.MESSAGE, number=3, message=timestamp.Timestamp,) + + +class SignBlobRequest(proto.Message): + r""" + + Attributes: + name (str): + Required. The resource name of the service account for which + the credentials are requested, in the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it with + a project ID is invalid. + delegates (Sequence[str]): + The sequence of service accounts in a delegation chain. Each + service account must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on its next + service account in the chain. The last service account in + the chain must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on the service + account that is specified in the ``name`` field of the + request. + + The delegates must have the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it with + a project ID is invalid. + payload (bytes): + Required. The bytes to sign. + """ + + name = proto.Field(proto.STRING, number=1) + + delegates = proto.RepeatedField(proto.STRING, number=3) + + payload = proto.Field(proto.BYTES, number=5) + + +class SignBlobResponse(proto.Message): + r""" + + Attributes: + key_id (str): + The ID of the key used to sign the blob. + signed_blob (bytes): + The signed blob. + """ + + key_id = proto.Field(proto.STRING, number=1) + + signed_blob = proto.Field(proto.BYTES, number=4) + + +class SignJwtRequest(proto.Message): + r""" + + Attributes: + name (str): + Required. The resource name of the service account for which + the credentials are requested, in the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it with + a project ID is invalid. + delegates (Sequence[str]): + The sequence of service accounts in a delegation chain. Each + service account must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on its next + service account in the chain. The last service account in + the chain must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on the service + account that is specified in the ``name`` field of the + request. + + The delegates must have the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it with + a project ID is invalid. + payload (str): + Required. The JWT payload to sign: a JSON + object that contains a JWT Claims Set. + """ + + name = proto.Field(proto.STRING, number=1) + + delegates = proto.RepeatedField(proto.STRING, number=3) + + payload = proto.Field(proto.STRING, number=5) + + +class SignJwtResponse(proto.Message): + r""" + + Attributes: + key_id (str): + The ID of the key used to sign the JWT. + signed_jwt (str): + The signed JWT. + """ + + key_id = proto.Field(proto.STRING, number=1) + + signed_jwt = proto.Field(proto.STRING, number=2) + + +class GenerateIdTokenRequest(proto.Message): + r""" + + Attributes: + name (str): + Required. The resource name of the service account for which + the credentials are requested, in the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it with + a project ID is invalid. + delegates (Sequence[str]): + The sequence of service accounts in a delegation chain. Each + service account must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on its next + service account in the chain. The last service account in + the chain must be granted the + ``roles/iam.serviceAccountTokenCreator`` role on the service + account that is specified in the ``name`` field of the + request. + + The delegates must have the following format: + ``projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}``. + The ``-`` wildcard character is required; replacing it with + a project ID is invalid. + audience (str): + Required. The audience for the token, such as + the API or account that this token grants access + to. + include_email (bool): + Include the service account email in the token. If set to + ``true``, the token will contain ``email`` and + ``email_verified`` claims. + """ + + name = proto.Field(proto.STRING, number=1) + + delegates = proto.RepeatedField(proto.STRING, number=2) + + audience = proto.Field(proto.STRING, number=3) + + include_email = proto.Field(proto.BOOL, number=4) + + +class GenerateIdTokenResponse(proto.Message): + r""" + + Attributes: + token (str): + The OpenId Connect ID token. + """ + + token = proto.Field(proto.STRING, number=1) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/__init__.py b/google/cloud/iam_credentials_v1/types/iamcredentials.py similarity index 71% rename from google/__init__.py rename to google/cloud/iam_credentials_v1/types/iamcredentials.py index 9a1b64a..64cd49a 100644 --- a/google/__init__.py +++ b/google/cloud/iam_credentials_v1/types/iamcredentials.py @@ -1,24 +1,22 @@ # -*- coding: utf-8 -*- -# + # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# + -try: - import pkg_resources +__protobuf__ = proto.module(package="google.iam.credentials.v1", manifest={},) - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - __path__ = pkgutil.extend_path(__path__, __name__) +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/noxfile.py b/noxfile.py index df5b36a..3a6dc54 100644 --- a/noxfile.py +++ b/noxfile.py @@ -27,8 +27,8 @@ BLACK_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] DEFAULT_PYTHON_VERSION = "3.8" -SYSTEM_TEST_PYTHON_VERSIONS = ["2.7", "3.8"] -UNIT_TEST_PYTHON_VERSIONS = ["2.7", "3.5", "3.6", "3.7", "3.8"] +SYSTEM_TEST_PYTHON_VERSIONS = ["3.8"] +UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8"] @nox.session(python=DEFAULT_PYTHON_VERSION) @@ -70,6 +70,8 @@ def lint_setup_py(session): def default(session): # Install all test dependencies, then install this package in-place. + session.install("asyncmock", "pytest-asyncio") + session.install("mock", "pytest", "pytest-cov") session.install("-e", ".") @@ -77,7 +79,7 @@ def default(session): session.run( "py.test", "--quiet", - "--cov=google.cloud.iam", + "--cov=google.cloud.iam_credentials_v1", "--cov=google.cloud", "--cov=tests.unit", "--cov-append", @@ -135,7 +137,7 @@ def cover(session): test runs (not system test runs), and then erases coverage data. """ session.install("coverage", "pytest-cov") - session.run("coverage", "report", "--show-missing", "--fail-under=76") + session.run("coverage", "report", "--show-missing", "--fail-under=99") session.run("coverage", "erase") diff --git a/samples/AUTHORING_GUIDE.md b/samples/AUTHORING_GUIDE.md new file mode 100644 index 0000000..55c97b3 --- /dev/null +++ b/samples/AUTHORING_GUIDE.md @@ -0,0 +1 @@ +See https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/AUTHORING_GUIDE.md \ No newline at end of file diff --git a/samples/CONTRIBUTING.md b/samples/CONTRIBUTING.md new file mode 100644 index 0000000..34c882b --- /dev/null +++ b/samples/CONTRIBUTING.md @@ -0,0 +1 @@ +See https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/CONTRIBUTING.md \ No newline at end of file diff --git a/scripts/fixup_credentials_v1_keywords.py b/scripts/fixup_credentials_v1_keywords.py new file mode 100644 index 0000000..5c0c330 --- /dev/null +++ b/scripts/fixup_credentials_v1_keywords.py @@ -0,0 +1,181 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import argparse +import os +import libcst as cst +import pathlib +import sys +from typing import (Any, Callable, Dict, List, Sequence, Tuple) + + +def partition( + predicate: Callable[[Any], bool], + iterator: Sequence[Any] +) -> Tuple[List[Any], List[Any]]: + """A stable, out-of-place partition.""" + results = ([], []) + + for i in iterator: + results[int(predicate(i))].append(i) + + # Returns trueList, falseList + return results[1], results[0] + + +class credentialsCallTransformer(cst.CSTTransformer): + CTRL_PARAMS: Tuple[str] = ('retry', 'timeout', 'metadata') + METHOD_TO_PARAMS: Dict[str, Tuple[str]] = { + 'generate_access_token': ('name', 'scope', 'delegates', 'lifetime', ), + 'generate_id_token': ('name', 'audience', 'delegates', 'include_email', ), + 'sign_blob': ('name', 'payload', 'delegates', ), + 'sign_jwt': ('name', 'payload', 'delegates', ), + + } + + def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode: + try: + key = original.func.attr.value + kword_params = self.METHOD_TO_PARAMS[key] + except (AttributeError, KeyError): + # Either not a method from the API or too convoluted to be sure. + return updated + + # If the existing code is valid, keyword args come after positional args. + # Therefore, all positional args must map to the first parameters. + args, kwargs = partition(lambda a: not bool(a.keyword), updated.args) + if any(k.keyword.value == "request" for k in kwargs): + # We've already fixed this file, don't fix it again. + return updated + + kwargs, ctrl_kwargs = partition( + lambda a: not a.keyword.value in self.CTRL_PARAMS, + kwargs + ) + + args, ctrl_args = args[:len(kword_params)], args[len(kword_params):] + ctrl_kwargs.extend(cst.Arg(value=a.value, keyword=cst.Name(value=ctrl)) + for a, ctrl in zip(ctrl_args, self.CTRL_PARAMS)) + + request_arg = cst.Arg( + value=cst.Dict([ + cst.DictElement( + cst.SimpleString("'{}'".format(name)), + cst.Element(value=arg.value) + ) + # Note: the args + kwargs looks silly, but keep in mind that + # the control parameters had to be stripped out, and that + # those could have been passed positionally or by keyword. + for name, arg in zip(kword_params, args + kwargs)]), + keyword=cst.Name("request") + ) + + return updated.with_changes( + args=[request_arg] + ctrl_kwargs + ) + + +def fix_files( + in_dir: pathlib.Path, + out_dir: pathlib.Path, + *, + transformer=credentialsCallTransformer(), +): + """Duplicate the input dir to the output dir, fixing file method calls. + + Preconditions: + * in_dir is a real directory + * out_dir is a real, empty directory + """ + pyfile_gen = ( + pathlib.Path(os.path.join(root, f)) + for root, _, files in os.walk(in_dir) + for f in files if os.path.splitext(f)[1] == ".py" + ) + + for fpath in pyfile_gen: + with open(fpath, 'r') as f: + src = f.read() + + # Parse the code and insert method call fixes. + tree = cst.parse_module(src) + updated = tree.visit(transformer) + + # Create the path and directory structure for the new file. + updated_path = out_dir.joinpath(fpath.relative_to(in_dir)) + updated_path.parent.mkdir(parents=True, exist_ok=True) + + # Generate the updated source file at the corresponding path. + with open(updated_path, 'w') as f: + f.write(updated.code) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description="""Fix up source that uses the credentials client library. + +The existing sources are NOT overwritten but are copied to output_dir with changes made. + +Note: This tool operates at a best-effort level at converting positional + parameters in client method calls to keyword based parameters. + Cases where it WILL FAIL include + A) * or ** expansion in a method call. + B) Calls via function or method alias (includes free function calls) + C) Indirect or dispatched calls (e.g. the method is looked up dynamically) + + These all constitute false negatives. The tool will also detect false + positives when an API method shares a name with another method. +""") + parser.add_argument( + '-d', + '--input-directory', + required=True, + dest='input_dir', + help='the input directory to walk for python files to fix up', + ) + parser.add_argument( + '-o', + '--output-directory', + required=True, + dest='output_dir', + help='the directory to output files fixed via un-flattening', + ) + args = parser.parse_args() + input_dir = pathlib.Path(args.input_dir) + output_dir = pathlib.Path(args.output_dir) + if not input_dir.is_dir(): + print( + f"input directory '{input_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if not output_dir.is_dir(): + print( + f"output directory '{output_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if os.listdir(output_dir): + print( + f"output directory '{output_dir}' is not empty", + file=sys.stderr, + ) + sys.exit(-1) + + fix_files(input_dir, output_dir) diff --git a/setup.py b/setup.py index fe6b266..927014f 100644 --- a/setup.py +++ b/setup.py @@ -28,8 +28,9 @@ # 'Development Status :: 5 - Production/Stable' release_status = "Development Status :: 5 - Production/Stable" dependencies = [ - "google-api-core[grpc] >= 1.14.0, < 2.0.0dev", - 'enum34; python_version < "3.4"', + "google-api-core[grpc] >= 1.22.0, < 2.0.0dev", + "proto-plus >= 0.4.0", + "libcst >= 0.2.5", ] package_root = os.path.abspath(os.path.dirname(__file__)) @@ -39,7 +40,9 @@ readme = readme_file.read() packages = [ - package for package in setuptools.find_packages() if package.startswith("google") + package + for package in setuptools.PEP420PackageFinder.find() + if package.startswith("google") ] namespaces = ["google"] @@ -60,12 +63,10 @@ "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Operating System :: OS Independent", "Topic :: Internet", ], @@ -73,7 +74,8 @@ packages=packages, namespace_packages=namespaces, install_requires=dependencies, - python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*", + python_requires=">=3.6", + scripts=["scripts/fixup_credentials_v1_keywords.py"], include_package_data=True, zip_safe=False, ) diff --git a/synth.metadata b/synth.metadata index 23a5d82..7fdcf2e 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,22 +4,14 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/python-iam.git", - "sha": "58c1f2e7d59501e1e237908a70e7457ce73565da" - } - }, - { - "git": { - "name": "googleapis", - "remote": "https://github.com/googleapis/googleapis.git", - "sha": "b882b8e6bfcd708042ff00f7adc67ce750817dd0", - "internalRef": "318028816" + "sha": "fb57fe375bf121ffd2e16e4cd6c8cc432624e271" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "303271797a360f8a439203413f13a160f2f5b3b4" + "sha": "5506723fef367557d83cf072d0c18020c049fcb8" } } ], diff --git a/synth.py b/synth.py index ec38dd0..9322c45 100644 --- a/synth.py +++ b/synth.py @@ -30,47 +30,52 @@ include_protos=True, ) -excludes = [ - "README.rst", - "setup.py", - "docs/index.rst", - "nox.py", -] -s.copy(library, excludes=excludes) +s.move(library / "google/cloud/iam_credentials_v1/proto") +s.move(library / "google/iam/credentials/", "google/cloud/iam_credentials") +s.move(library / "google/iam/credentials_v1", "google/cloud/iam_credentials_v1") +s.move(library / "tests") +s.move(library / "scripts") +s.move(library / "docs", excludes=[library / "docs/index.rst"]) +# Fix namespace s.replace( - "google/**/*.py", - "google-cloud-iam-credentials", - "google-cloud-iam" + "google/cloud/**/*.py", + "google.iam.credentials_v1", + "google.cloud.iam_credentials_v1", ) s.replace( - "docs/**/*.py", - "google-cloud-iam-credentials", - "google-cloud-iam" -) - -s.replace( - "**/*.py", - "from google\.iam\.credentials\.v1 import common_pb2", - "from google.cloud.iam_credentials_v1.proto import common_pb2" + "tests/unit/gapic/**/*.py", + "google.iam.credentials_v1", + "google.cloud.iam_credentials_v1", ) s.replace( - "**/*.py", - "from google\.iam\.credentials\.v1 import iamcredentials_pb2_grpc", - "from google.cloud.iam_credentials_v1.proto import iamcredentials_pb2_grpc" + "docs/**/*.rst", + "google.iam.credentials_v1", + "google.cloud.iam_credentials_v1", ) +# Rename package to `google-cloud-build` s.replace( - "google/cloud/iam_credentials_v1/proto/common_pb2.py", - "\"\"\"Attributes:\n", - "\"\"\"\nAttributes:\n" + ["**/*.rst", "*/**/*.py", "**/*.md"], + "google-iam-credentials", + "google-cloud-iam" ) # ---------------------------------------------------------------------------- # Add templated files # ---------------------------------------------------------------------------- -templated_files = common.py_library(cov_level=76) -s.move(templated_files) +templated_files = common.py_library( + samples=False, # set to True only if there are samples + microgenerator=True, + cov_level=99, +) +s.move(templated_files, excludes=[".coveragerc"]) # microgenerator has a good .coveragerc file + +s.replace( + "noxfile.py", + "google.cloud.iam", + "google.cloud.iam_credentials_v1", +) # TODO(busunkim): Use latest sphinx after microgenerator transition s.replace("noxfile.py", """['"]sphinx['"]""", '"sphinx<3.0.0"') diff --git a/tests/unit/gapic/credentials_v1/test_iam_credentials.py b/tests/unit/gapic/credentials_v1/test_iam_credentials.py new file mode 100644 index 0000000..f01b0aa --- /dev/null +++ b/tests/unit/gapic/credentials_v1/test_iam_credentials.py @@ -0,0 +1,1522 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import os +import mock + +import grpc +from grpc.experimental import aio +import math +import pytest +from proto.marshal.rules.dates import DurationRule, TimestampRule + +from google import auth +from google.api_core import client_options +from google.api_core import exceptions +from google.api_core import grpc_helpers +from google.api_core import grpc_helpers_async +from google.auth import credentials +from google.auth.exceptions import MutualTLSChannelError +from google.cloud.iam_credentials_v1.services.iam_credentials import ( + IAMCredentialsAsyncClient, +) +from google.cloud.iam_credentials_v1.services.iam_credentials import ( + IAMCredentialsClient, +) +from google.cloud.iam_credentials_v1.services.iam_credentials import transports +from google.cloud.iam_credentials_v1.types import common +from google.oauth2 import service_account +from google.protobuf import duration_pb2 as duration # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + + +def client_cert_source_callback(): + return b"cert bytes", b"key bytes" + + +def test__get_default_mtls_endpoint(): + api_endpoint = "example.googleapis.com" + api_mtls_endpoint = "example.mtls.googleapis.com" + sandbox_endpoint = "example.sandbox.googleapis.com" + sandbox_mtls_endpoint = "example.mtls.sandbox.googleapis.com" + non_googleapi = "api.example.com" + + assert IAMCredentialsClient._get_default_mtls_endpoint(None) is None + assert ( + IAMCredentialsClient._get_default_mtls_endpoint(api_endpoint) + == api_mtls_endpoint + ) + assert ( + IAMCredentialsClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + IAMCredentialsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + IAMCredentialsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + IAMCredentialsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + ) + + +@pytest.mark.parametrize( + "client_class", [IAMCredentialsClient, IAMCredentialsAsyncClient] +) +def test_iam_credentials_client_from_service_account_file(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_file" + ) as factory: + factory.return_value = creds + client = client_class.from_service_account_file("dummy/file/path.json") + assert client._transport._credentials == creds + + client = client_class.from_service_account_json("dummy/file/path.json") + assert client._transport._credentials == creds + + assert client._transport._host == "iamcredentials.googleapis.com:443" + + +def test_iam_credentials_client_get_transport_class(): + transport = IAMCredentialsClient.get_transport_class() + assert transport == transports.IAMCredentialsGrpcTransport + + transport = IAMCredentialsClient.get_transport_class("grpc") + assert transport == transports.IAMCredentialsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (IAMCredentialsClient, transports.IAMCredentialsGrpcTransport, "grpc"), + ( + IAMCredentialsAsyncClient, + transports.IAMCredentialsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_iam_credentials_client_client_options( + client_class, transport_class, transport_name +): + # Check that if channel is provided we won't create a new one. + with mock.patch.object(IAMCredentialsClient, "get_transport_class") as gtc: + transport = transport_class(credentials=credentials.AnonymousCredentials()) + client = client_class(transport=transport) + gtc.assert_not_called() + + # Check that if channel is provided via str we will create a new one. + with mock.patch.object(IAMCredentialsClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + api_mtls_endpoint="squid.clam.whelk", + client_cert_source=None, + quota_project_id=None, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + api_mtls_endpoint=client.DEFAULT_ENDPOINT, + client_cert_source=None, + quota_project_id=None, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, + client_cert_source=None, + quota_project_id=None, + ) + + # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is + # "auto", and client_cert_source is provided. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, + client_cert_source=client_cert_source_callback, + quota_project_id=None, + ) + + # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is + # "auto", and default_client_cert_source is provided. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, + client_cert_source=None, + quota_project_id=None, + ) + + # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is + # "auto", but client_cert_source and default_client_cert_source are None. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + api_mtls_endpoint=client.DEFAULT_ENDPOINT, + client_cert_source=None, + quota_project_id=None, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + api_mtls_endpoint=client.DEFAULT_ENDPOINT, + client_cert_source=None, + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (IAMCredentialsClient, transports.IAMCredentialsGrpcTransport, "grpc"), + ( + IAMCredentialsAsyncClient, + transports.IAMCredentialsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_iam_credentials_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + api_mtls_endpoint=client.DEFAULT_ENDPOINT, + client_cert_source=None, + quota_project_id=None, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (IAMCredentialsClient, transports.IAMCredentialsGrpcTransport, "grpc"), + ( + IAMCredentialsAsyncClient, + transports.IAMCredentialsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_iam_credentials_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + api_mtls_endpoint=client.DEFAULT_ENDPOINT, + client_cert_source=None, + quota_project_id=None, + ) + + +def test_iam_credentials_client_client_options_from_dict(): + with mock.patch( + "google.cloud.iam_credentials_v1.services.iam_credentials.transports.IAMCredentialsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = IAMCredentialsClient( + client_options={"api_endpoint": "squid.clam.whelk"} + ) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + api_mtls_endpoint="squid.clam.whelk", + client_cert_source=None, + quota_project_id=None, + ) + + +def test_generate_access_token(transport: str = "grpc"): + client = IAMCredentialsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = common.GenerateAccessTokenRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._transport.generate_access_token), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = common.GenerateAccessTokenResponse( + access_token="access_token_value", + ) + + response = client.generate_access_token(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == request + + # Establish that the response is the type that we expect. + assert isinstance(response, common.GenerateAccessTokenResponse) + + assert response.access_token == "access_token_value" + + +@pytest.mark.asyncio +async def test_generate_access_token_async(transport: str = "grpc_asyncio"): + client = IAMCredentialsAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = common.GenerateAccessTokenRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.generate_access_token), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + common.GenerateAccessTokenResponse(access_token="access_token_value",) + ) + + response = await client.generate_access_token(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == request + + # Establish that the response is the type that we expect. + assert isinstance(response, common.GenerateAccessTokenResponse) + + assert response.access_token == "access_token_value" + + +def test_generate_access_token_field_headers(): + client = IAMCredentialsClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = common.GenerateAccessTokenRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._transport.generate_access_token), "__call__" + ) as call: + call.return_value = common.GenerateAccessTokenResponse() + + client.generate_access_token(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_generate_access_token_field_headers_async(): + client = IAMCredentialsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = common.GenerateAccessTokenRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.generate_access_token), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + common.GenerateAccessTokenResponse() + ) + + await client.generate_access_token(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_generate_access_token_flattened(): + client = IAMCredentialsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._transport.generate_access_token), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = common.GenerateAccessTokenResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.generate_access_token( + name="name_value", + delegates=["delegates_value"], + scope=["scope_value"], + lifetime=duration.Duration(seconds=751), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].name == "name_value" + + assert args[0].delegates == ["delegates_value"] + + assert args[0].scope == ["scope_value"] + + assert DurationRule().to_proto(args[0].lifetime) == duration.Duration( + seconds=751 + ) + + +def test_generate_access_token_flattened_error(): + client = IAMCredentialsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.generate_access_token( + common.GenerateAccessTokenRequest(), + name="name_value", + delegates=["delegates_value"], + scope=["scope_value"], + lifetime=duration.Duration(seconds=751), + ) + + +@pytest.mark.asyncio +async def test_generate_access_token_flattened_async(): + client = IAMCredentialsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.generate_access_token), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = common.GenerateAccessTokenResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + common.GenerateAccessTokenResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.generate_access_token( + name="name_value", + delegates=["delegates_value"], + scope=["scope_value"], + lifetime=duration.Duration(seconds=751), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].name == "name_value" + + assert args[0].delegates == ["delegates_value"] + + assert args[0].scope == ["scope_value"] + + assert DurationRule().to_proto(args[0].lifetime) == duration.Duration( + seconds=751 + ) + + +@pytest.mark.asyncio +async def test_generate_access_token_flattened_error_async(): + client = IAMCredentialsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.generate_access_token( + common.GenerateAccessTokenRequest(), + name="name_value", + delegates=["delegates_value"], + scope=["scope_value"], + lifetime=duration.Duration(seconds=751), + ) + + +def test_generate_id_token(transport: str = "grpc"): + client = IAMCredentialsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = common.GenerateIdTokenRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._transport.generate_id_token), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = common.GenerateIdTokenResponse(token="token_value",) + + response = client.generate_id_token(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == request + + # Establish that the response is the type that we expect. + assert isinstance(response, common.GenerateIdTokenResponse) + + assert response.token == "token_value" + + +@pytest.mark.asyncio +async def test_generate_id_token_async(transport: str = "grpc_asyncio"): + client = IAMCredentialsAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = common.GenerateIdTokenRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.generate_id_token), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + common.GenerateIdTokenResponse(token="token_value",) + ) + + response = await client.generate_id_token(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == request + + # Establish that the response is the type that we expect. + assert isinstance(response, common.GenerateIdTokenResponse) + + assert response.token == "token_value" + + +def test_generate_id_token_field_headers(): + client = IAMCredentialsClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = common.GenerateIdTokenRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._transport.generate_id_token), "__call__" + ) as call: + call.return_value = common.GenerateIdTokenResponse() + + client.generate_id_token(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_generate_id_token_field_headers_async(): + client = IAMCredentialsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = common.GenerateIdTokenRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.generate_id_token), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + common.GenerateIdTokenResponse() + ) + + await client.generate_id_token(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_generate_id_token_flattened(): + client = IAMCredentialsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._transport.generate_id_token), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = common.GenerateIdTokenResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.generate_id_token( + name="name_value", + delegates=["delegates_value"], + audience="audience_value", + include_email=True, + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].name == "name_value" + + assert args[0].delegates == ["delegates_value"] + + assert args[0].audience == "audience_value" + + assert args[0].include_email == True + + +def test_generate_id_token_flattened_error(): + client = IAMCredentialsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.generate_id_token( + common.GenerateIdTokenRequest(), + name="name_value", + delegates=["delegates_value"], + audience="audience_value", + include_email=True, + ) + + +@pytest.mark.asyncio +async def test_generate_id_token_flattened_async(): + client = IAMCredentialsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.generate_id_token), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = common.GenerateIdTokenResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + common.GenerateIdTokenResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.generate_id_token( + name="name_value", + delegates=["delegates_value"], + audience="audience_value", + include_email=True, + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].name == "name_value" + + assert args[0].delegates == ["delegates_value"] + + assert args[0].audience == "audience_value" + + assert args[0].include_email == True + + +@pytest.mark.asyncio +async def test_generate_id_token_flattened_error_async(): + client = IAMCredentialsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.generate_id_token( + common.GenerateIdTokenRequest(), + name="name_value", + delegates=["delegates_value"], + audience="audience_value", + include_email=True, + ) + + +def test_sign_blob(transport: str = "grpc"): + client = IAMCredentialsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = common.SignBlobRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client._transport.sign_blob), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = common.SignBlobResponse( + key_id="key_id_value", signed_blob=b"signed_blob_blob", + ) + + response = client.sign_blob(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == request + + # Establish that the response is the type that we expect. + assert isinstance(response, common.SignBlobResponse) + + assert response.key_id == "key_id_value" + + assert response.signed_blob == b"signed_blob_blob" + + +@pytest.mark.asyncio +async def test_sign_blob_async(transport: str = "grpc_asyncio"): + client = IAMCredentialsAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = common.SignBlobRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.sign_blob), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + common.SignBlobResponse( + key_id="key_id_value", signed_blob=b"signed_blob_blob", + ) + ) + + response = await client.sign_blob(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == request + + # Establish that the response is the type that we expect. + assert isinstance(response, common.SignBlobResponse) + + assert response.key_id == "key_id_value" + + assert response.signed_blob == b"signed_blob_blob" + + +def test_sign_blob_field_headers(): + client = IAMCredentialsClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = common.SignBlobRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client._transport.sign_blob), "__call__") as call: + call.return_value = common.SignBlobResponse() + + client.sign_blob(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_sign_blob_field_headers_async(): + client = IAMCredentialsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = common.SignBlobRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.sign_blob), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + common.SignBlobResponse() + ) + + await client.sign_blob(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_sign_blob_flattened(): + client = IAMCredentialsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client._transport.sign_blob), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = common.SignBlobResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.sign_blob( + name="name_value", delegates=["delegates_value"], payload=b"payload_blob", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].name == "name_value" + + assert args[0].delegates == ["delegates_value"] + + assert args[0].payload == b"payload_blob" + + +def test_sign_blob_flattened_error(): + client = IAMCredentialsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.sign_blob( + common.SignBlobRequest(), + name="name_value", + delegates=["delegates_value"], + payload=b"payload_blob", + ) + + +@pytest.mark.asyncio +async def test_sign_blob_flattened_async(): + client = IAMCredentialsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.sign_blob), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = common.SignBlobResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + common.SignBlobResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.sign_blob( + name="name_value", delegates=["delegates_value"], payload=b"payload_blob", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].name == "name_value" + + assert args[0].delegates == ["delegates_value"] + + assert args[0].payload == b"payload_blob" + + +@pytest.mark.asyncio +async def test_sign_blob_flattened_error_async(): + client = IAMCredentialsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.sign_blob( + common.SignBlobRequest(), + name="name_value", + delegates=["delegates_value"], + payload=b"payload_blob", + ) + + +def test_sign_jwt(transport: str = "grpc"): + client = IAMCredentialsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = common.SignJwtRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client._transport.sign_jwt), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = common.SignJwtResponse( + key_id="key_id_value", signed_jwt="signed_jwt_value", + ) + + response = client.sign_jwt(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == request + + # Establish that the response is the type that we expect. + assert isinstance(response, common.SignJwtResponse) + + assert response.key_id == "key_id_value" + + assert response.signed_jwt == "signed_jwt_value" + + +@pytest.mark.asyncio +async def test_sign_jwt_async(transport: str = "grpc_asyncio"): + client = IAMCredentialsAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = common.SignJwtRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.sign_jwt), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + common.SignJwtResponse( + key_id="key_id_value", signed_jwt="signed_jwt_value", + ) + ) + + response = await client.sign_jwt(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == request + + # Establish that the response is the type that we expect. + assert isinstance(response, common.SignJwtResponse) + + assert response.key_id == "key_id_value" + + assert response.signed_jwt == "signed_jwt_value" + + +def test_sign_jwt_field_headers(): + client = IAMCredentialsClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = common.SignJwtRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client._transport.sign_jwt), "__call__") as call: + call.return_value = common.SignJwtResponse() + + client.sign_jwt(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_sign_jwt_field_headers_async(): + client = IAMCredentialsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = common.SignJwtRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.sign_jwt), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + common.SignJwtResponse() + ) + + await client.sign_jwt(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] + + +def test_sign_jwt_flattened(): + client = IAMCredentialsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client._transport.sign_jwt), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = common.SignJwtResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.sign_jwt( + name="name_value", delegates=["delegates_value"], payload="payload_value", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].name == "name_value" + + assert args[0].delegates == ["delegates_value"] + + assert args[0].payload == "payload_value" + + +def test_sign_jwt_flattened_error(): + client = IAMCredentialsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.sign_jwt( + common.SignJwtRequest(), + name="name_value", + delegates=["delegates_value"], + payload="payload_value", + ) + + +@pytest.mark.asyncio +async def test_sign_jwt_flattened_async(): + client = IAMCredentialsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client._client._transport.sign_jwt), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = common.SignJwtResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + common.SignJwtResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.sign_jwt( + name="name_value", delegates=["delegates_value"], payload="payload_value", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].name == "name_value" + + assert args[0].delegates == ["delegates_value"] + + assert args[0].payload == "payload_value" + + +@pytest.mark.asyncio +async def test_sign_jwt_flattened_error_async(): + client = IAMCredentialsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.sign_jwt( + common.SignJwtRequest(), + name="name_value", + delegates=["delegates_value"], + payload="payload_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.IAMCredentialsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = IAMCredentialsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.IAMCredentialsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = IAMCredentialsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.IAMCredentialsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = IAMCredentialsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.IAMCredentialsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = IAMCredentialsClient(transport=transport) + assert client._transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.IAMCredentialsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.IAMCredentialsGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +def test_transport_grpc_default(): + # A client should use the gRPC transport by default. + client = IAMCredentialsClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client._transport, transports.IAMCredentialsGrpcTransport,) + + +def test_iam_credentials_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.IAMCredentialsTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_iam_credentials_base_transport(): + # Instantiate the base transport. + transport = transports.IAMCredentialsTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "generate_access_token", + "generate_id_token", + "sign_blob", + "sign_jwt", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_iam_credentials_base_transport_with_credentials_file(): + # Instantiate the base transport with a credentials file + with mock.patch.object(auth, "load_credentials_from_file") as load_creds: + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.IAMCredentialsTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=("https://www.googleapis.com/auth/cloud-platform",), + quota_project_id="octopus", + ) + + +def test_iam_credentials_auth_adc(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + IAMCredentialsClient() + adc.assert_called_once_with( + scopes=("https://www.googleapis.com/auth/cloud-platform",), + quota_project_id=None, + ) + + +def test_iam_credentials_transport_auth_adc(): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + transports.IAMCredentialsGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=("https://www.googleapis.com/auth/cloud-platform",), + quota_project_id="octopus", + ) + + +def test_iam_credentials_host_no_port(): + client = IAMCredentialsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="iamcredentials.googleapis.com" + ), + ) + assert client._transport._host == "iamcredentials.googleapis.com:443" + + +def test_iam_credentials_host_with_port(): + client = IAMCredentialsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="iamcredentials.googleapis.com:8000" + ), + ) + assert client._transport._host == "iamcredentials.googleapis.com:8000" + + +def test_iam_credentials_grpc_transport_channel(): + channel = grpc.insecure_channel("http://localhost/") + + # Check that if channel is provided, mtls endpoint and client_cert_source + # won't be used. + callback = mock.MagicMock() + transport = transports.IAMCredentialsGrpcTransport( + host="squid.clam.whelk", + channel=channel, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=callback, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert not callback.called + + +def test_iam_credentials_grpc_asyncio_transport_channel(): + channel = aio.insecure_channel("http://localhost/") + + # Check that if channel is provided, mtls endpoint and client_cert_source + # won't be used. + callback = mock.MagicMock() + transport = transports.IAMCredentialsGrpcAsyncIOTransport( + host="squid.clam.whelk", + channel=channel, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=callback, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert not callback.called + + +@mock.patch("grpc.ssl_channel_credentials", autospec=True) +@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) +def test_iam_credentials_grpc_transport_channel_mtls_with_client_cert_source( + grpc_create_channel, grpc_ssl_channel_cred +): + # Check that if channel is None, but api_mtls_endpoint and client_cert_source + # are provided, then a mTLS channel will be created. + mock_cred = mock.Mock() + + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + transport = transports.IAMCredentialsGrpcTransport( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=("https://www.googleapis.com/auth/cloud-platform",), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +@mock.patch("grpc.ssl_channel_credentials", autospec=True) +@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) +def test_iam_credentials_grpc_asyncio_transport_channel_mtls_with_client_cert_source( + grpc_create_channel, grpc_ssl_channel_cred +): + # Check that if channel is None, but api_mtls_endpoint and client_cert_source + # are provided, then a mTLS channel will be created. + mock_cred = mock.Mock() + + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + transport = transports.IAMCredentialsGrpcAsyncIOTransport( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=("https://www.googleapis.com/auth/cloud-platform",), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +@pytest.mark.parametrize( + "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] +) +@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) +def test_iam_credentials_grpc_transport_channel_mtls_with_adc( + grpc_create_channel, api_mtls_endpoint +): + # Check that if channel and client_cert_source are None, but api_mtls_endpoint + # is provided, then a mTLS channel will be created with SSL ADC. + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + # Mock google.auth.transport.grpc.SslCredentials class. + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + mock_cred = mock.Mock() + transport = transports.IAMCredentialsGrpcTransport( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint=api_mtls_endpoint, + client_cert_source=None, + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=("https://www.googleapis.com/auth/cloud-platform",), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +@pytest.mark.parametrize( + "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] +) +@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) +def test_iam_credentials_grpc_asyncio_transport_channel_mtls_with_adc( + grpc_create_channel, api_mtls_endpoint +): + # Check that if channel and client_cert_source are None, but api_mtls_endpoint + # is provided, then a mTLS channel will be created with SSL ADC. + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + # Mock google.auth.transport.grpc.SslCredentials class. + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + mock_cred = mock.Mock() + transport = transports.IAMCredentialsGrpcAsyncIOTransport( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint=api_mtls_endpoint, + client_cert_source=None, + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=("https://www.googleapis.com/auth/cloud-platform",), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel diff --git a/tests/unit/gapic/v1/test_iam_credentials_client_v1.py b/tests/unit/gapic/v1/test_iam_credentials_client_v1.py deleted file mode 100644 index b90f179..0000000 --- a/tests/unit/gapic/v1/test_iam_credentials_client_v1.py +++ /dev/null @@ -1,225 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Unit tests.""" - -import mock -import pytest - -from google.cloud import iam_credentials_v1 -from google.cloud.iam_credentials_v1.proto import common_pb2 - - -class MultiCallableStub(object): - """Stub for the grpc.UnaryUnaryMultiCallable interface.""" - - def __init__(self, method, channel_stub): - self.method = method - self.channel_stub = channel_stub - - def __call__(self, request, timeout=None, metadata=None, credentials=None): - self.channel_stub.requests.append((self.method, request)) - - response = None - if self.channel_stub.responses: - response = self.channel_stub.responses.pop() - - if isinstance(response, Exception): - raise response - - if response: - return response - - -class ChannelStub(object): - """Stub for the grpc.Channel interface.""" - - def __init__(self, responses=[]): - self.responses = responses - self.requests = [] - - def unary_unary(self, method, request_serializer=None, response_deserializer=None): - return MultiCallableStub(method, self) - - -class CustomException(Exception): - pass - - -class TestIAMCredentialsClient(object): - def test_generate_access_token(self): - # Setup Expected Response - access_token = "accessToken-1938933922" - expected_response = {"access_token": access_token} - expected_response = common_pb2.GenerateAccessTokenResponse(**expected_response) - - # Mock the API response - channel = ChannelStub(responses=[expected_response]) - patch = mock.patch("google.api_core.grpc_helpers.create_channel") - with patch as create_channel: - create_channel.return_value = channel - client = iam_credentials_v1.IAMCredentialsClient() - - # Setup Request - name = client.service_account_path("[PROJECT]", "[SERVICE_ACCOUNT]") - scope = [] - - response = client.generate_access_token(name, scope) - assert expected_response == response - - assert len(channel.requests) == 1 - expected_request = common_pb2.GenerateAccessTokenRequest(name=name, scope=scope) - actual_request = channel.requests[0][1] - assert expected_request == actual_request - - def test_generate_access_token_exception(self): - # Mock the API response - channel = ChannelStub(responses=[CustomException()]) - patch = mock.patch("google.api_core.grpc_helpers.create_channel") - with patch as create_channel: - create_channel.return_value = channel - client = iam_credentials_v1.IAMCredentialsClient() - - # Setup request - name = client.service_account_path("[PROJECT]", "[SERVICE_ACCOUNT]") - scope = [] - - with pytest.raises(CustomException): - client.generate_access_token(name, scope) - - def test_generate_id_token(self): - # Setup Expected Response - token = "token110541305" - expected_response = {"token": token} - expected_response = common_pb2.GenerateIdTokenResponse(**expected_response) - - # Mock the API response - channel = ChannelStub(responses=[expected_response]) - patch = mock.patch("google.api_core.grpc_helpers.create_channel") - with patch as create_channel: - create_channel.return_value = channel - client = iam_credentials_v1.IAMCredentialsClient() - - # Setup Request - name = client.service_account_path("[PROJECT]", "[SERVICE_ACCOUNT]") - audience = "audience975628804" - - response = client.generate_id_token(name, audience) - assert expected_response == response - - assert len(channel.requests) == 1 - expected_request = common_pb2.GenerateIdTokenRequest( - name=name, audience=audience - ) - actual_request = channel.requests[0][1] - assert expected_request == actual_request - - def test_generate_id_token_exception(self): - # Mock the API response - channel = ChannelStub(responses=[CustomException()]) - patch = mock.patch("google.api_core.grpc_helpers.create_channel") - with patch as create_channel: - create_channel.return_value = channel - client = iam_credentials_v1.IAMCredentialsClient() - - # Setup request - name = client.service_account_path("[PROJECT]", "[SERVICE_ACCOUNT]") - audience = "audience975628804" - - with pytest.raises(CustomException): - client.generate_id_token(name, audience) - - def test_sign_blob(self): - # Setup Expected Response - key_id = "keyId-1134673157" - signed_blob = b"-32" - expected_response = {"key_id": key_id, "signed_blob": signed_blob} - expected_response = common_pb2.SignBlobResponse(**expected_response) - - # Mock the API response - channel = ChannelStub(responses=[expected_response]) - patch = mock.patch("google.api_core.grpc_helpers.create_channel") - with patch as create_channel: - create_channel.return_value = channel - client = iam_credentials_v1.IAMCredentialsClient() - - # Setup Request - name = client.service_account_path("[PROJECT]", "[SERVICE_ACCOUNT]") - payload = b"-114" - - response = client.sign_blob(name, payload) - assert expected_response == response - - assert len(channel.requests) == 1 - expected_request = common_pb2.SignBlobRequest(name=name, payload=payload) - actual_request = channel.requests[0][1] - assert expected_request == actual_request - - def test_sign_blob_exception(self): - # Mock the API response - channel = ChannelStub(responses=[CustomException()]) - patch = mock.patch("google.api_core.grpc_helpers.create_channel") - with patch as create_channel: - create_channel.return_value = channel - client = iam_credentials_v1.IAMCredentialsClient() - - # Setup request - name = client.service_account_path("[PROJECT]", "[SERVICE_ACCOUNT]") - payload = b"-114" - - with pytest.raises(CustomException): - client.sign_blob(name, payload) - - def test_sign_jwt(self): - # Setup Expected Response - key_id = "keyId-1134673157" - signed_jwt = "signedJwt-979546844" - expected_response = {"key_id": key_id, "signed_jwt": signed_jwt} - expected_response = common_pb2.SignJwtResponse(**expected_response) - - # Mock the API response - channel = ChannelStub(responses=[expected_response]) - patch = mock.patch("google.api_core.grpc_helpers.create_channel") - with patch as create_channel: - create_channel.return_value = channel - client = iam_credentials_v1.IAMCredentialsClient() - - # Setup Request - name = client.service_account_path("[PROJECT]", "[SERVICE_ACCOUNT]") - payload = "-114" - - response = client.sign_jwt(name, payload) - assert expected_response == response - - assert len(channel.requests) == 1 - expected_request = common_pb2.SignJwtRequest(name=name, payload=payload) - actual_request = channel.requests[0][1] - assert expected_request == actual_request - - def test_sign_jwt_exception(self): - # Mock the API response - channel = ChannelStub(responses=[CustomException()]) - patch = mock.patch("google.api_core.grpc_helpers.create_channel") - with patch as create_channel: - create_channel.return_value = channel - client = iam_credentials_v1.IAMCredentialsClient() - - # Setup request - name = client.service_account_path("[PROJECT]", "[SERVICE_ACCOUNT]") - payload = "-114" - - with pytest.raises(CustomException): - client.sign_jwt(name, payload)