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

Commit ac2fe87

Browse files
feat: add api key support (#320)
* 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 b8ef3eb commit ac2fe87

File tree

3 files changed

+245
-44
lines changed

3 files changed

+245
-44
lines changed

google/cloud/dlp_v2/services/dlp_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
@@ -129,6 +129,42 @@ def from_service_account_file(cls, filename: str, *args, **kwargs):
129129

130130
from_service_account_json = from_service_account_file
131131

132+
@classmethod
133+
def get_mtls_endpoint_and_cert_source(
134+
cls, client_options: Optional[ClientOptions] = None
135+
):
136+
"""Return the API endpoint and client cert source for mutual TLS.
137+
138+
The client cert source is determined in the following order:
139+
(1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the
140+
client cert source is None.
141+
(2) if `client_options.client_cert_source` is provided, use the provided one; if the
142+
default client cert source exists, use the default one; otherwise the client cert
143+
source is None.
144+
145+
The API endpoint is determined in the following order:
146+
(1) if `client_options.api_endpoint` if provided, use the provided one.
147+
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
148+
default mTLS endpoint; if the environment variabel is "never", use the default API
149+
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
150+
use the default API endpoint.
151+
152+
More details can be found at https://google.aip.dev/auth/4114.
153+
154+
Args:
155+
client_options (google.api_core.client_options.ClientOptions): Custom options for the
156+
client. Only the `api_endpoint` and `client_cert_source` properties may be used
157+
in this method.
158+
159+
Returns:
160+
Tuple[str, Callable[[], Tuple[bytes, bytes]]]: returns the API endpoint and the
161+
client cert source to use.
162+
163+
Raises:
164+
google.auth.exceptions.MutualTLSChannelError: If any errors happen.
165+
"""
166+
return DlpServiceClient.get_mtls_endpoint_and_cert_source(client_options) # type: ignore
167+
132168
@property
133169
def transport(self) -> DlpServiceTransport:
134170
"""Returns the transport used by the client instance.

google/cloud/dlp_v2/services/dlp_service/client.py

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

333+
@classmethod
334+
def get_mtls_endpoint_and_cert_source(
335+
cls, client_options: Optional[client_options_lib.ClientOptions] = None
336+
):
337+
"""Return the API endpoint and client cert source for mutual TLS.
338+
339+
The client cert source is determined in the following order:
340+
(1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the
341+
client cert source is None.
342+
(2) if `client_options.client_cert_source` is provided, use the provided one; if the
343+
default client cert source exists, use the default one; otherwise the client cert
344+
source is None.
345+
346+
The API endpoint is determined in the following order:
347+
(1) if `client_options.api_endpoint` if provided, use the provided one.
348+
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
349+
default mTLS endpoint; if the environment variabel is "never", use the default API
350+
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
351+
use the default API endpoint.
352+
353+
More details can be found at https://google.aip.dev/auth/4114.
354+
355+
Args:
356+
client_options (google.api_core.client_options.ClientOptions): Custom options for the
357+
client. Only the `api_endpoint` and `client_cert_source` properties may be used
358+
in this method.
359+
360+
Returns:
361+
Tuple[str, Callable[[], Tuple[bytes, bytes]]]: returns the API endpoint and the
362+
client cert source to use.
363+
364+
Raises:
365+
google.auth.exceptions.MutualTLSChannelError: If any errors happen.
366+
"""
367+
if client_options is None:
368+
client_options = client_options_lib.ClientOptions()
369+
use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
370+
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
371+
if use_client_cert not in ("true", "false"):
372+
raise ValueError(
373+
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
374+
)
375+
if use_mtls_endpoint not in ("auto", "never", "always"):
376+
raise MutualTLSChannelError(
377+
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
378+
)
379+
380+
# Figure out the client cert source to use.
381+
client_cert_source = None
382+
if use_client_cert == "true":
383+
if client_options.client_cert_source:
384+
client_cert_source = client_options.client_cert_source
385+
elif mtls.has_default_client_cert_source():
386+
client_cert_source = mtls.default_client_cert_source()
387+
388+
# Figure out which api endpoint to use.
389+
if client_options.api_endpoint is not None:
390+
api_endpoint = client_options.api_endpoint
391+
elif use_mtls_endpoint == "always" or (
392+
use_mtls_endpoint == "auto" and client_cert_source
393+
):
394+
api_endpoint = cls.DEFAULT_MTLS_ENDPOINT
395+
else:
396+
api_endpoint = cls.DEFAULT_ENDPOINT
397+
398+
return api_endpoint, client_cert_source
399+
333400
def __init__(
334401
self,
335402
*,
@@ -380,57 +447,22 @@ def __init__(
380447
if client_options is None:
381448
client_options = client_options_lib.ClientOptions()
382449

383-
# Create SSL credentials for mutual TLS if needed.
384-
if os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") not in (
385-
"true",
386-
"false",
387-
):
388-
raise ValueError(
389-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
390-
)
391-
use_client_cert = (
392-
os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true"
450+
api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source(
451+
client_options
393452
)
394453

395-
client_cert_source_func = None
396-
is_mtls = False
397-
if use_client_cert:
398-
if client_options.client_cert_source:
399-
is_mtls = True
400-
client_cert_source_func = client_options.client_cert_source
401-
else:
402-
is_mtls = mtls.has_default_client_cert_source()
403-
if is_mtls:
404-
client_cert_source_func = mtls.default_client_cert_source()
405-
else:
406-
client_cert_source_func = None
407-
408-
# Figure out which api endpoint to use.
409-
if client_options.api_endpoint is not None:
410-
api_endpoint = client_options.api_endpoint
411-
else:
412-
use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
413-
if use_mtls_env == "never":
414-
api_endpoint = self.DEFAULT_ENDPOINT
415-
elif use_mtls_env == "always":
416-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
417-
elif use_mtls_env == "auto":
418-
if is_mtls:
419-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
420-
else:
421-
api_endpoint = self.DEFAULT_ENDPOINT
422-
else:
423-
raise MutualTLSChannelError(
424-
"Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted "
425-
"values: never, auto, always"
426-
)
454+
api_key_value = getattr(client_options, "api_key", None)
455+
if api_key_value and credentials:
456+
raise ValueError(
457+
"client_options.api_key and credentials are mutually exclusive"
458+
)
427459

428460
# Save or instantiate the transport.
429461
# Ordinarily, we provide the transport, but allowing a custom transport
430462
# instance provides an extensibility point for unusual situations.
431463
if isinstance(transport, DlpServiceTransport):
432464
# transport is a DlpServiceTransport instance.
433-
if credentials or client_options.credentials_file:
465+
if credentials or client_options.credentials_file or api_key_value:
434466
raise ValueError(
435467
"When providing a transport instance, "
436468
"provide its credentials directly."
@@ -442,6 +474,15 @@ def __init__(
442474
)
443475
self._transport = transport
444476
else:
477+
import google.auth._default # type: ignore
478+
479+
if api_key_value and hasattr(
480+
google.auth._default, "get_api_key_credentials"
481+
):
482+
credentials = google.auth._default.get_api_key_credentials(
483+
api_key_value
484+
)
485+
445486
Transport = type(self).get_transport_class(transport)
446487
self._transport = Transport(
447488
credentials=credentials,

tests/unit/gapic/dlp_v2/test_dlp_service.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,83 @@ def test_dlp_service_client_mtls_env_auto(
393393
)
394394

395395

396+
@pytest.mark.parametrize("client_class", [DlpServiceClient, DlpServiceAsyncClient])
397+
@mock.patch.object(
398+
DlpServiceClient, "DEFAULT_ENDPOINT", modify_default_endpoint(DlpServiceClient)
399+
)
400+
@mock.patch.object(
401+
DlpServiceAsyncClient,
402+
"DEFAULT_ENDPOINT",
403+
modify_default_endpoint(DlpServiceAsyncClient),
404+
)
405+
def test_dlp_service_client_get_mtls_endpoint_and_cert_source(client_class):
406+
mock_client_cert_source = mock.Mock()
407+
408+
# Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "true".
409+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
410+
mock_api_endpoint = "foo"
411+
options = client_options.ClientOptions(
412+
client_cert_source=mock_client_cert_source, api_endpoint=mock_api_endpoint
413+
)
414+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source(
415+
options
416+
)
417+
assert api_endpoint == mock_api_endpoint
418+
assert cert_source == mock_client_cert_source
419+
420+
# Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "false".
421+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}):
422+
mock_client_cert_source = mock.Mock()
423+
mock_api_endpoint = "foo"
424+
options = client_options.ClientOptions(
425+
client_cert_source=mock_client_cert_source, api_endpoint=mock_api_endpoint
426+
)
427+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source(
428+
options
429+
)
430+
assert api_endpoint == mock_api_endpoint
431+
assert cert_source is None
432+
433+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "never".
434+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
435+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
436+
assert api_endpoint == client_class.DEFAULT_ENDPOINT
437+
assert cert_source is None
438+
439+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "always".
440+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}):
441+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
442+
assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
443+
assert cert_source is None
444+
445+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "auto" and default cert doesn't exist.
446+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
447+
with mock.patch(
448+
"google.auth.transport.mtls.has_default_client_cert_source",
449+
return_value=False,
450+
):
451+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
452+
assert api_endpoint == client_class.DEFAULT_ENDPOINT
453+
assert cert_source is None
454+
455+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "auto" and default cert exists.
456+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
457+
with mock.patch(
458+
"google.auth.transport.mtls.has_default_client_cert_source",
459+
return_value=True,
460+
):
461+
with mock.patch(
462+
"google.auth.transport.mtls.default_client_cert_source",
463+
return_value=mock_client_cert_source,
464+
):
465+
(
466+
api_endpoint,
467+
cert_source,
468+
) = client_class.get_mtls_endpoint_and_cert_source()
469+
assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
470+
assert cert_source == mock_client_cert_source
471+
472+
396473
@pytest.mark.parametrize(
397474
"client_class,transport_class,transport_name",
398475
[
@@ -8008,6 +8085,23 @@ def test_credentials_transport_error():
80088085
transport=transport,
80098086
)
80108087

8088+
# It is an error to provide an api_key and a transport instance.
8089+
transport = transports.DlpServiceGrpcTransport(
8090+
credentials=ga_credentials.AnonymousCredentials(),
8091+
)
8092+
options = client_options.ClientOptions()
8093+
options.api_key = "api_key"
8094+
with pytest.raises(ValueError):
8095+
client = DlpServiceClient(client_options=options, transport=transport,)
8096+
8097+
# It is an error to provide an api_key and a credential.
8098+
options = mock.Mock()
8099+
options.api_key = "api_key"
8100+
with pytest.raises(ValueError):
8101+
client = DlpServiceClient(
8102+
client_options=options, credentials=ga_credentials.AnonymousCredentials()
8103+
)
8104+
80118105
# It is an error to provide scopes and a transport instance.
80128106
transport = transports.DlpServiceGrpcTransport(
80138107
credentials=ga_credentials.AnonymousCredentials(),
@@ -8711,3 +8805,33 @@ def test_client_ctx():
87118805
with client:
87128806
pass
87138807
close.assert_called()
8808+
8809+
8810+
@pytest.mark.parametrize(
8811+
"client_class,transport_class",
8812+
[
8813+
(DlpServiceClient, transports.DlpServiceGrpcTransport),
8814+
(DlpServiceAsyncClient, transports.DlpServiceGrpcAsyncIOTransport),
8815+
],
8816+
)
8817+
def test_api_key_credentials(client_class, transport_class):
8818+
with mock.patch.object(
8819+
google.auth._default, "get_api_key_credentials", create=True
8820+
) as get_api_key_credentials:
8821+
mock_cred = mock.Mock()
8822+
get_api_key_credentials.return_value = mock_cred
8823+
options = client_options.ClientOptions()
8824+
options.api_key = "api_key"
8825+
with mock.patch.object(transport_class, "__init__") as patched:
8826+
patched.return_value = None
8827+
client = client_class(client_options=options)
8828+
patched.assert_called_once_with(
8829+
credentials=mock_cred,
8830+
credentials_file=None,
8831+
host=client.DEFAULT_ENDPOINT,
8832+
scopes=None,
8833+
client_cert_source_for_mtls=None,
8834+
quota_project_id=None,
8835+
client_info=transports.base.DEFAULT_CLIENT_INFO,
8836+
always_use_jwt_access=True,
8837+
)

0 commit comments

Comments
 (0)