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

Commit 27517d5

Browse files
feat: add api key support (#3)
* 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 3a6ab23 commit 27517d5

File tree

3 files changed

+245
-44
lines changed

3 files changed

+245
-44
lines changed

google/cloud/eventarc_publishing_v1/services/publisher/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
@@ -121,6 +121,42 @@ def from_service_account_file(cls, filename: str, *args, **kwargs):
121121

122122
from_service_account_json = from_service_account_file
123123

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

google/cloud/eventarc_publishing_v1/services/publisher/client.py

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

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

292-
# Create SSL credentials for mutual TLS if needed.
293-
if os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") not in (
294-
"true",
295-
"false",
296-
):
297-
raise ValueError(
298-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
299-
)
300-
use_client_cert = (
301-
os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true"
359+
api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source(
360+
client_options
302361
)
303362

304-
client_cert_source_func = None
305-
is_mtls = False
306-
if use_client_cert:
307-
if client_options.client_cert_source:
308-
is_mtls = True
309-
client_cert_source_func = client_options.client_cert_source
310-
else:
311-
is_mtls = mtls.has_default_client_cert_source()
312-
if is_mtls:
313-
client_cert_source_func = mtls.default_client_cert_source()
314-
else:
315-
client_cert_source_func = None
316-
317-
# Figure out which api endpoint to use.
318-
if client_options.api_endpoint is not None:
319-
api_endpoint = client_options.api_endpoint
320-
else:
321-
use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
322-
if use_mtls_env == "never":
323-
api_endpoint = self.DEFAULT_ENDPOINT
324-
elif use_mtls_env == "always":
325-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
326-
elif use_mtls_env == "auto":
327-
if is_mtls:
328-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
329-
else:
330-
api_endpoint = self.DEFAULT_ENDPOINT
331-
else:
332-
raise MutualTLSChannelError(
333-
"Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted "
334-
"values: never, auto, always"
335-
)
363+
api_key_value = getattr(client_options, "api_key", None)
364+
if api_key_value and credentials:
365+
raise ValueError(
366+
"client_options.api_key and credentials are mutually exclusive"
367+
)
336368

337369
# Save or instantiate the transport.
338370
# Ordinarily, we provide the transport, but allowing a custom transport
339371
# instance provides an extensibility point for unusual situations.
340372
if isinstance(transport, PublisherTransport):
341373
# transport is a PublisherTransport instance.
342-
if credentials or client_options.credentials_file:
374+
if credentials or client_options.credentials_file or api_key_value:
343375
raise ValueError(
344376
"When providing a transport instance, "
345377
"provide its credentials directly."
@@ -351,6 +383,15 @@ def __init__(
351383
)
352384
self._transport = transport
353385
else:
386+
import google.auth._default # type: ignore
387+
388+
if api_key_value and hasattr(
389+
google.auth._default, "get_api_key_credentials"
390+
):
391+
credentials = google.auth._default.get_api_key_credentials(
392+
api_key_value
393+
)
394+
354395
Transport = type(self).get_transport_class(transport)
355396
self._transport = Transport(
356397
credentials=credentials,

tests/unit/gapic/eventarc_publishing_v1/test_publisher.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,83 @@ def test_publisher_client_mtls_env_auto(
380380
)
381381

382382

383+
@pytest.mark.parametrize("client_class", [PublisherClient, PublisherAsyncClient])
384+
@mock.patch.object(
385+
PublisherClient, "DEFAULT_ENDPOINT", modify_default_endpoint(PublisherClient)
386+
)
387+
@mock.patch.object(
388+
PublisherAsyncClient,
389+
"DEFAULT_ENDPOINT",
390+
modify_default_endpoint(PublisherAsyncClient),
391+
)
392+
def test_publisher_client_get_mtls_endpoint_and_cert_source(client_class):
393+
mock_client_cert_source = mock.Mock()
394+
395+
# Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "true".
396+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
397+
mock_api_endpoint = "foo"
398+
options = client_options.ClientOptions(
399+
client_cert_source=mock_client_cert_source, api_endpoint=mock_api_endpoint
400+
)
401+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source(
402+
options
403+
)
404+
assert api_endpoint == mock_api_endpoint
405+
assert cert_source == mock_client_cert_source
406+
407+
# Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "false".
408+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}):
409+
mock_client_cert_source = mock.Mock()
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 is None
419+
420+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "never".
421+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
422+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
423+
assert api_endpoint == client_class.DEFAULT_ENDPOINT
424+
assert cert_source is None
425+
426+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "always".
427+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}):
428+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
429+
assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
430+
assert cert_source is None
431+
432+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "auto" and default cert doesn't exist.
433+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
434+
with mock.patch(
435+
"google.auth.transport.mtls.has_default_client_cert_source",
436+
return_value=False,
437+
):
438+
api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source()
439+
assert api_endpoint == client_class.DEFAULT_ENDPOINT
440+
assert cert_source is None
441+
442+
# Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "auto" and default cert exists.
443+
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
444+
with mock.patch(
445+
"google.auth.transport.mtls.has_default_client_cert_source",
446+
return_value=True,
447+
):
448+
with mock.patch(
449+
"google.auth.transport.mtls.default_client_cert_source",
450+
return_value=mock_client_cert_source,
451+
):
452+
(
453+
api_endpoint,
454+
cert_source,
455+
) = client_class.get_mtls_endpoint_and_cert_source()
456+
assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT
457+
assert cert_source == mock_client_cert_source
458+
459+
383460
@pytest.mark.parametrize(
384461
"client_class,transport_class,transport_name",
385462
[
@@ -624,6 +701,23 @@ def test_credentials_transport_error():
624701
transport=transport,
625702
)
626703

704+
# It is an error to provide an api_key and a transport instance.
705+
transport = transports.PublisherGrpcTransport(
706+
credentials=ga_credentials.AnonymousCredentials(),
707+
)
708+
options = client_options.ClientOptions()
709+
options.api_key = "api_key"
710+
with pytest.raises(ValueError):
711+
client = PublisherClient(client_options=options, transport=transport,)
712+
713+
# It is an error to provide an api_key and a credential.
714+
options = mock.Mock()
715+
options.api_key = "api_key"
716+
with pytest.raises(ValueError):
717+
client = PublisherClient(
718+
client_options=options, credentials=ga_credentials.AnonymousCredentials()
719+
)
720+
627721
# It is an error to provide scopes and a transport instance.
628722
transport = transports.PublisherGrpcTransport(
629723
credentials=ga_credentials.AnonymousCredentials(),
@@ -1140,3 +1234,33 @@ def test_client_ctx():
11401234
with client:
11411235
pass
11421236
close.assert_called()
1237+
1238+
1239+
@pytest.mark.parametrize(
1240+
"client_class,transport_class",
1241+
[
1242+
(PublisherClient, transports.PublisherGrpcTransport),
1243+
(PublisherAsyncClient, transports.PublisherGrpcAsyncIOTransport),
1244+
],
1245+
)
1246+
def test_api_key_credentials(client_class, transport_class):
1247+
with mock.patch.object(
1248+
google.auth._default, "get_api_key_credentials", create=True
1249+
) as get_api_key_credentials:
1250+
mock_cred = mock.Mock()
1251+
get_api_key_credentials.return_value = mock_cred
1252+
options = client_options.ClientOptions()
1253+
options.api_key = "api_key"
1254+
with mock.patch.object(transport_class, "__init__") as patched:
1255+
patched.return_value = None
1256+
client = client_class(client_options=options)
1257+
patched.assert_called_once_with(
1258+
credentials=mock_cred,
1259+
credentials_file=None,
1260+
host=client.DEFAULT_ENDPOINT,
1261+
scopes=None,
1262+
client_cert_source_for_mtls=None,
1263+
quota_project_id=None,
1264+
client_info=transports.base.DEFAULT_CLIENT_INFO,
1265+
always_use_jwt_access=True,
1266+
)

0 commit comments

Comments
 (0)