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

Commit 91b316a

Browse files
feat: add api key support (#103)
* 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 88d3f4c commit 91b316a

File tree

3 files changed

+260
-44
lines changed

3 files changed

+260
-44
lines changed

google/cloud/managedidentities_v1/services/managed_identities_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
@@ -151,6 +151,42 @@ def from_service_account_file(cls, filename: str, *args, **kwargs):
151151

152152
from_service_account_json = from_service_account_file
153153

154+
@classmethod
155+
def get_mtls_endpoint_and_cert_source(
156+
cls, client_options: Optional[ClientOptions] = None
157+
):
158+
"""Return the API endpoint and client cert source for mutual TLS.
159+
160+
The client cert source is determined in the following order:
161+
(1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the
162+
client cert source is None.
163+
(2) if `client_options.client_cert_source` is provided, use the provided one; if the
164+
default client cert source exists, use the default one; otherwise the client cert
165+
source is None.
166+
167+
The API endpoint is determined in the following order:
168+
(1) if `client_options.api_endpoint` if provided, use the provided one.
169+
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
170+
default mTLS endpoint; if the environment variabel is "never", use the default API
171+
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
172+
use the default API endpoint.
173+
174+
More details can be found at https://google.aip.dev/auth/4114.
175+
176+
Args:
177+
client_options (google.api_core.client_options.ClientOptions): Custom options for the
178+
client. Only the `api_endpoint` and `client_cert_source` properties may be used
179+
in this method.
180+
181+
Returns:
182+
Tuple[str, Callable[[], Tuple[bytes, bytes]]]: returns the API endpoint and the
183+
client cert source to use.
184+
185+
Raises:
186+
google.auth.exceptions.MutualTLSChannelError: If any errors happen.
187+
"""
188+
return ManagedIdentitiesServiceClient.get_mtls_endpoint_and_cert_source(client_options) # type: ignore
189+
154190
@property
155191
def transport(self) -> ManagedIdentitiesServiceTransport:
156192
"""Returns the transport used by the client instance.

google/cloud/managedidentities_v1/services/managed_identities_service/client.py

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

280+
@classmethod
281+
def get_mtls_endpoint_and_cert_source(
282+
cls, client_options: Optional[client_options_lib.ClientOptions] = None
283+
):
284+
"""Return the API endpoint and client cert source for mutual TLS.
285+
286+
The client cert source is determined in the following order:
287+
(1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the
288+
client cert source is None.
289+
(2) if `client_options.client_cert_source` is provided, use the provided one; if the
290+
default client cert source exists, use the default one; otherwise the client cert
291+
source is None.
292+
293+
The API endpoint is determined in the following order:
294+
(1) if `client_options.api_endpoint` if provided, use the provided one.
295+
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
296+
default mTLS endpoint; if the environment variabel is "never", use the default API
297+
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
298+
use the default API endpoint.
299+
300+
More details can be found at https://google.aip.dev/auth/4114.
301+
302+
Args:
303+
client_options (google.api_core.client_options.ClientOptions): Custom options for the
304+
client. Only the `api_endpoint` and `client_cert_source` properties may be used
305+
in this method.
306+
307+
Returns:
308+
Tuple[str, Callable[[], Tuple[bytes, bytes]]]: returns the API endpoint and the
309+
client cert source to use.
310+
311+
Raises:
312+
google.auth.exceptions.MutualTLSChannelError: If any errors happen.
313+
"""
314+
if client_options is None:
315+
client_options = client_options_lib.ClientOptions()
316+
use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
317+
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
318+
if use_client_cert not in ("true", "false"):
319+
raise ValueError(
320+
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
321+
)
322+
if use_mtls_endpoint not in ("auto", "never", "always"):
323+
raise MutualTLSChannelError(
324+
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
325+
)
326+
327+
# Figure out the client cert source to use.
328+
client_cert_source = None
329+
if use_client_cert == "true":
330+
if client_options.client_cert_source:
331+
client_cert_source = client_options.client_cert_source
332+
elif mtls.has_default_client_cert_source():
333+
client_cert_source = mtls.default_client_cert_source()
334+
335+
# Figure out which api endpoint to use.
336+
if client_options.api_endpoint is not None:
337+
api_endpoint = client_options.api_endpoint
338+
elif use_mtls_endpoint == "always" or (
339+
use_mtls_endpoint == "auto" and client_cert_source
340+
):
341+
api_endpoint = cls.DEFAULT_MTLS_ENDPOINT
342+
else:
343+
api_endpoint = cls.DEFAULT_ENDPOINT
344+
345+
return api_endpoint, client_cert_source
346+
280347
def __init__(
281348
self,
282349
*,
@@ -327,57 +394,22 @@ def __init__(
327394
if client_options is None:
328395
client_options = client_options_lib.ClientOptions()
329396

330-
# Create SSL credentials for mutual TLS if needed.
331-
if os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") not in (
332-
"true",
333-
"false",
334-
):
335-
raise ValueError(
336-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
337-
)
338-
use_client_cert = (
339-
os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true"
397+
api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source(
398+
client_options
340399
)
341400

342-
client_cert_source_func = None
343-
is_mtls = False
344-
if use_client_cert:
345-
if client_options.client_cert_source:
346-
is_mtls = True
347-
client_cert_source_func = client_options.client_cert_source
348-
else:
349-
is_mtls = mtls.has_default_client_cert_source()
350-
if is_mtls:
351-
client_cert_source_func = mtls.default_client_cert_source()
352-
else:
353-
client_cert_source_func = None
354-
355-
# Figure out which api endpoint to use.
356-
if client_options.api_endpoint is not None:
357-
api_endpoint = client_options.api_endpoint
358-
else:
359-
use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
360-
if use_mtls_env == "never":
361-
api_endpoint = self.DEFAULT_ENDPOINT
362-
elif use_mtls_env == "always":
363-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
364-
elif use_mtls_env == "auto":
365-
if is_mtls:
366-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
367-
else:
368-
api_endpoint = self.DEFAULT_ENDPOINT
369-
else:
370-
raise MutualTLSChannelError(
371-
"Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted "
372-
"values: never, auto, always"
373-
)
401+
api_key_value = getattr(client_options, "api_key", None)
402+
if api_key_value and credentials:
403+
raise ValueError(
404+
"client_options.api_key and credentials are mutually exclusive"
405+
)
374406

375407
# Save or instantiate the transport.
376408
# Ordinarily, we provide the transport, but allowing a custom transport
377409
# instance provides an extensibility point for unusual situations.
378410
if isinstance(transport, ManagedIdentitiesServiceTransport):
379411
# transport is a ManagedIdentitiesServiceTransport instance.
380-
if credentials or client_options.credentials_file:
412+
if credentials or client_options.credentials_file or api_key_value:
381413
raise ValueError(
382414
"When providing a transport instance, "
383415
"provide its credentials directly."
@@ -389,6 +421,15 @@ def __init__(
389421
)
390422
self._transport = transport
391423
else:
424+
import google.auth._default # type: ignore
425+
426+
if api_key_value and hasattr(
427+
google.auth._default, "get_api_key_credentials"
428+
):
429+
credentials = google.auth._default.get_api_key_credentials(
430+
api_key_value
431+
)
432+
392433
Transport = type(self).get_transport_class(transport)
393434
self._transport = Transport(
394435
credentials=credentials,

tests/unit/gapic/managedidentities_v1/test_managed_identities_service.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,90 @@ def test_managed_identities_service_client_mtls_env_auto(
429429
)
430430

431431

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

3047+
# It is an error to provide an api_key and a transport instance.
3048+
transport = transports.ManagedIdentitiesServiceGrpcTransport(
3049+
credentials=ga_credentials.AnonymousCredentials(),
3050+
)
3051+
options = client_options.ClientOptions()
3052+
options.api_key = "api_key"
3053+
with pytest.raises(ValueError):
3054+
client = ManagedIdentitiesServiceClient(
3055+
client_options=options, transport=transport,
3056+
)
3057+
3058+
# It is an error to provide an api_key and a credential.
3059+
options = mock.Mock()
3060+
options.api_key = "api_key"
3061+
with pytest.raises(ValueError):
3062+
client = ManagedIdentitiesServiceClient(
3063+
client_options=options, credentials=ga_credentials.AnonymousCredentials()
3064+
)
3065+
29633066
# It is an error to provide scopes and a transport instance.
29643067
transport = transports.ManagedIdentitiesServiceGrpcTransport(
29653068
credentials=ga_credentials.AnonymousCredentials(),
@@ -3567,3 +3670,39 @@ def test_client_ctx():
35673670
with client:
35683671
pass
35693672
close.assert_called()
3673+
3674+
3675+
@pytest.mark.parametrize(
3676+
"client_class,transport_class",
3677+
[
3678+
(
3679+
ManagedIdentitiesServiceClient,
3680+
transports.ManagedIdentitiesServiceGrpcTransport,
3681+
),
3682+
(
3683+
ManagedIdentitiesServiceAsyncClient,
3684+
transports.ManagedIdentitiesServiceGrpcAsyncIOTransport,
3685+
),
3686+
],
3687+
)
3688+
def test_api_key_credentials(client_class, transport_class):
3689+
with mock.patch.object(
3690+
google.auth._default, "get_api_key_credentials", create=True
3691+
) as get_api_key_credentials:
3692+
mock_cred = mock.Mock()
3693+
get_api_key_credentials.return_value = mock_cred
3694+
options = client_options.ClientOptions()
3695+
options.api_key = "api_key"
3696+
with mock.patch.object(transport_class, "__init__") as patched:
3697+
patched.return_value = None
3698+
client = client_class(client_options=options)
3699+
patched.assert_called_once_with(
3700+
credentials=mock_cred,
3701+
credentials_file=None,
3702+
host=client.DEFAULT_ENDPOINT,
3703+
scopes=None,
3704+
client_cert_source_for_mtls=None,
3705+
quota_project_id=None,
3706+
client_info=transports.base.DEFAULT_CLIENT_INFO,
3707+
always_use_jwt_access=True,
3708+
)

0 commit comments

Comments
 (0)