Skip to content
This repository was archived by the owner on Sep 29, 2023. It is now read-only.

Commit 156018b

Browse files
feat: add api key support (#52)
* chore: upgrade gapic-generator-java, gax-java and gapic-generator-python PiperOrigin-RevId: 423842556 Source-Link: googleapis/googleapis@a616ca0 Source-Link: https://github.com/googleapis/googleapis-gen/commit/29b938c58c1e51d019f2ee539d55dc0a3c86a905 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMjliOTM4YzU4YzFlNTFkMDE5ZjJlZTUzOWQ1NWRjMGEzYzg2YTkwNSJ9 * 🦉 Updates from OwlBot See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent dee67e8 commit 156018b

File tree

3 files changed

+256
-44
lines changed

3 files changed

+256
-44
lines changed

google/cloud/storage_transfer_v1/services/storage_transfer_service/async_client.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from collections import OrderedDict
1717
import functools
1818
import re
19-
from typing import Dict, Sequence, Tuple, Type, Union
19+
from typing import Dict, Optional, Sequence, Tuple, Type, Union
2020
import pkg_resources
2121

2222
from google.api_core.client_options import ClientOptions
@@ -114,6 +114,42 @@ def from_service_account_file(cls, filename: str, *args, **kwargs):
114114

115115
from_service_account_json = from_service_account_file
116116

117+
@classmethod
118+
def get_mtls_endpoint_and_cert_source(
119+
cls, client_options: Optional[ClientOptions] = None
120+
):
121+
"""Return the API endpoint and client cert source for mutual TLS.
122+
123+
The client cert source is determined in the following order:
124+
(1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the
125+
client cert source is None.
126+
(2) if `client_options.client_cert_source` is provided, use the provided one; if the
127+
default client cert source exists, use the default one; otherwise the client cert
128+
source is None.
129+
130+
The API endpoint is determined in the following order:
131+
(1) if `client_options.api_endpoint` if provided, use the provided one.
132+
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
133+
default mTLS endpoint; if the environment variabel is "never", use the default API
134+
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
135+
use the default API endpoint.
136+
137+
More details can be found at https://google.aip.dev/auth/4114.
138+
139+
Args:
140+
client_options (google.api_core.client_options.ClientOptions): Custom options for the
141+
client. Only the `api_endpoint` and `client_cert_source` properties may be used
142+
in this method.
143+
144+
Returns:
145+
Tuple[str, Callable[[], Tuple[bytes, bytes]]]: returns the API endpoint and the
146+
client cert source to use.
147+
148+
Raises:
149+
google.auth.exceptions.MutualTLSChannelError: If any errors happen.
150+
"""
151+
return StorageTransferServiceClient.get_mtls_endpoint_and_cert_source(client_options) # type: ignore
152+
117153
@property
118154
def transport(self) -> StorageTransferServiceTransport:
119155
"""Returns the transport used by the client instance.

google/cloud/storage_transfer_v1/services/storage_transfer_service/client.py

Lines changed: 84 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,73 @@ def parse_common_location_path(path: str) -> Dict[str, str]:
228228
m = re.match(r"^projects/(?P<project>.+?)/locations/(?P<location>.+?)$", path)
229229
return m.groupdict() if m else {}
230230

231+
@classmethod
232+
def get_mtls_endpoint_and_cert_source(
233+
cls, client_options: Optional[client_options_lib.ClientOptions] = None
234+
):
235+
"""Return the API endpoint and client cert source for mutual TLS.
236+
237+
The client cert source is determined in the following order:
238+
(1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the
239+
client cert source is None.
240+
(2) if `client_options.client_cert_source` is provided, use the provided one; if the
241+
default client cert source exists, use the default one; otherwise the client cert
242+
source is None.
243+
244+
The API endpoint is determined in the following order:
245+
(1) if `client_options.api_endpoint` if provided, use the provided one.
246+
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
247+
default mTLS endpoint; if the environment variabel is "never", use the default API
248+
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
249+
use the default API endpoint.
250+
251+
More details can be found at https://google.aip.dev/auth/4114.
252+
253+
Args:
254+
client_options (google.api_core.client_options.ClientOptions): Custom options for the
255+
client. Only the `api_endpoint` and `client_cert_source` properties may be used
256+
in this method.
257+
258+
Returns:
259+
Tuple[str, Callable[[], Tuple[bytes, bytes]]]: returns the API endpoint and the
260+
client cert source to use.
261+
262+
Raises:
263+
google.auth.exceptions.MutualTLSChannelError: If any errors happen.
264+
"""
265+
if client_options is None:
266+
client_options = client_options_lib.ClientOptions()
267+
use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
268+
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
269+
if use_client_cert not in ("true", "false"):
270+
raise ValueError(
271+
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
272+
)
273+
if use_mtls_endpoint not in ("auto", "never", "always"):
274+
raise MutualTLSChannelError(
275+
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
276+
)
277+
278+
# Figure out the client cert source to use.
279+
client_cert_source = None
280+
if use_client_cert == "true":
281+
if client_options.client_cert_source:
282+
client_cert_source = client_options.client_cert_source
283+
elif mtls.has_default_client_cert_source():
284+
client_cert_source = mtls.default_client_cert_source()
285+
286+
# Figure out which api endpoint to use.
287+
if client_options.api_endpoint is not None:
288+
api_endpoint = client_options.api_endpoint
289+
elif use_mtls_endpoint == "always" or (
290+
use_mtls_endpoint == "auto" and client_cert_source
291+
):
292+
api_endpoint = cls.DEFAULT_MTLS_ENDPOINT
293+
else:
294+
api_endpoint = cls.DEFAULT_ENDPOINT
295+
296+
return api_endpoint, client_cert_source
297+
231298
def __init__(
232299
self,
233300
*,
@@ -278,57 +345,22 @@ def __init__(
278345
if client_options is None:
279346
client_options = client_options_lib.ClientOptions()
280347

281-
# Create SSL credentials for mutual TLS if needed.
282-
if os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") not in (
283-
"true",
284-
"false",
285-
):
286-
raise ValueError(
287-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
288-
)
289-
use_client_cert = (
290-
os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true"
348+
api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source(
349+
client_options
291350
)
292351

293-
client_cert_source_func = None
294-
is_mtls = False
295-
if use_client_cert:
296-
if client_options.client_cert_source:
297-
is_mtls = True
298-
client_cert_source_func = client_options.client_cert_source
299-
else:
300-
is_mtls = mtls.has_default_client_cert_source()
301-
if is_mtls:
302-
client_cert_source_func = mtls.default_client_cert_source()
303-
else:
304-
client_cert_source_func = None
305-
306-
# Figure out which api endpoint to use.
307-
if client_options.api_endpoint is not None:
308-
api_endpoint = client_options.api_endpoint
309-
else:
310-
use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
311-
if use_mtls_env == "never":
312-
api_endpoint = self.DEFAULT_ENDPOINT
313-
elif use_mtls_env == "always":
314-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
315-
elif use_mtls_env == "auto":
316-
if is_mtls:
317-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
318-
else:
319-
api_endpoint = self.DEFAULT_ENDPOINT
320-
else:
321-
raise MutualTLSChannelError(
322-
"Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted "
323-
"values: never, auto, always"
324-
)
352+
api_key_value = getattr(client_options, "api_key", None)
353+
if api_key_value and credentials:
354+
raise ValueError(
355+
"client_options.api_key and credentials are mutually exclusive"
356+
)
325357

326358
# Save or instantiate the transport.
327359
# Ordinarily, we provide the transport, but allowing a custom transport
328360
# instance provides an extensibility point for unusual situations.
329361
if isinstance(transport, StorageTransferServiceTransport):
330362
# transport is a StorageTransferServiceTransport instance.
331-
if credentials or client_options.credentials_file:
363+
if credentials or client_options.credentials_file or api_key_value:
332364
raise ValueError(
333365
"When providing a transport instance, "
334366
"provide its credentials directly."
@@ -340,6 +372,15 @@ def __init__(
340372
)
341373
self._transport = transport
342374
else:
375+
import google.auth._default # type: ignore
376+
377+
if api_key_value and hasattr(
378+
google.auth._default, "get_api_key_credentials"
379+
):
380+
credentials = google.auth._default.get_api_key_credentials(
381+
api_key_value
382+
)
383+
343384
Transport = type(self).get_transport_class(transport)
344385
self._transport = Transport(
345386
credentials=credentials,

tests/unit/gapic/storage_transfer_v1/test_storage_transfer_service.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,89 @@ def test_storage_transfer_service_client_mtls_env_auto(
426426
)
427427

428428

429+
@pytest.mark.parametrize(
430+
"client_class", [StorageTransferServiceClient, StorageTransferServiceAsyncClient]
431+
)
432+
@mock.patch.object(
433+
StorageTransferServiceClient,
434+
"DEFAULT_ENDPOINT",
435+
modify_default_endpoint(StorageTransferServiceClient),
436+
)
437+
@mock.patch.object(
438+
StorageTransferServiceAsyncClient,
439+
"DEFAULT_ENDPOINT",
440+
modify_default_endpoint(StorageTransferServiceAsyncClient),
441+
)
442+
def test_storage_transfer_service_client_get_mtls_endpoint_and_cert_source(
443+
client_class,
444+
):
445+
mock_client_cert_source = mock.Mock()
446+
447+
# Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "true".
448+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
449+
mock_api_endpoint = "foo"
450+
options = client_options.ClientOptions(
451+
client_cert_source=mock_client_cert_source, api_endpoint=mock_api_endpoint
452+
)
453+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source(
454+
options
455+
)
456+
assert api_endpoint == mock_api_endpoint
457+
assert cert_source == mock_client_cert_source
458+
459+
# Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "false".
460+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}):
461+
mock_client_cert_source = mock.Mock()
462+
mock_api_endpoint = "foo"
463+
options = client_options.ClientOptions(
464+
client_cert_source=mock_client_cert_source, api_endpoint=mock_api_endpoint
465+
)
466+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source(
467+
options
468+
)
469+
assert api_endpoint == mock_api_endpoint
470+
assert cert_source is None
471+
472+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "never".
473+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
474+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
475+
assert api_endpoint == client_class.DEFAULT_ENDPOINT
476+
assert cert_source is None
477+
478+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "always".
479+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}):
480+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
481+
assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
482+
assert cert_source is None
483+
484+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "auto" and default cert doesn't exist.
485+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
486+
with mock.patch(
487+
"google.auth.transport.mtls.has_default_client_cert_source",
488+
return_value=False,
489+
):
490+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
491+
assert api_endpoint == client_class.DEFAULT_ENDPOINT
492+
assert cert_source is None
493+
494+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "auto" and default cert exists.
495+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
496+
with mock.patch(
497+
"google.auth.transport.mtls.has_default_client_cert_source",
498+
return_value=True,
499+
):
500+
with mock.patch(
501+
"google.auth.transport.mtls.default_client_cert_source",
502+
return_value=mock_client_cert_source,
503+
):
504+
(
505+
api_endpoint,
506+
cert_source,
507+
) = client_class.get_mtls_endpoint_and_cert_source()
508+
assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
509+
assert cert_source == mock_client_cert_source
510+
511+
429512
@pytest.mark.parametrize(
430513
"client_class,transport_class,transport_name",
431514
[
@@ -1692,6 +1775,25 @@ def test_credentials_transport_error():
16921775
transport=transport,
16931776
)
16941777

1778+
# It is an error to provide an api_key and a transport instance.
1779+
transport = transports.StorageTransferServiceGrpcTransport(
1780+
credentials=ga_credentials.AnonymousCredentials(),
1781+
)
1782+
options = client_options.ClientOptions()
1783+
options.api_key = "api_key"
1784+
with pytest.raises(ValueError):
1785+
client = StorageTransferServiceClient(
1786+
client_options=options, transport=transport,
1787+
)
1788+
1789+
# It is an error to provide an api_key and a credential.
1790+
options = mock.Mock()
1791+
options.api_key = "api_key"
1792+
with pytest.raises(ValueError):
1793+
client = StorageTransferServiceClient(
1794+
client_options=options, credentials=ga_credentials.AnonymousCredentials()
1795+
)
1796+
16951797
# It is an error to provide scopes and a transport instance.
16961798
transport = transports.StorageTransferServiceGrpcTransport(
16971799
credentials=ga_credentials.AnonymousCredentials(),
@@ -2271,3 +2373,36 @@ def test_client_ctx():
22712373
with client:
22722374
pass
22732375
close.assert_called()
2376+
2377+
2378+
@pytest.mark.parametrize(
2379+
"client_class,transport_class",
2380+
[
2381+
(StorageTransferServiceClient, transports.StorageTransferServiceGrpcTransport),
2382+
(
2383+
StorageTransferServiceAsyncClient,
2384+
transports.StorageTransferServiceGrpcAsyncIOTransport,
2385+
),
2386+
],
2387+
)
2388+
def test_api_key_credentials(client_class, transport_class):
2389+
with mock.patch.object(
2390+
google.auth._default, "get_api_key_credentials", create=True
2391+
) as get_api_key_credentials:
2392+
mock_cred = mock.Mock()
2393+
get_api_key_credentials.return_value = mock_cred
2394+
options = client_options.ClientOptions()
2395+
options.api_key = "api_key"
2396+
with mock.patch.object(transport_class, "__init__") as patched:
2397+
patched.return_value = None
2398+
client = client_class(client_options=options)
2399+
patched.assert_called_once_with(
2400+
credentials=mock_cred,
2401+
credentials_file=None,
2402+
host=client.DEFAULT_ENDPOINT,
2403+
scopes=None,
2404+
client_cert_source_for_mtls=None,
2405+
quota_project_id=None,
2406+
client_info=transports.base.DEFAULT_CLIENT_INFO,
2407+
always_use_jwt_access=True,
2408+
)

0 commit comments

Comments
 (0)