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

Commit 21285cc

Browse files
feat: add api key support (#92)
* 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 cd7d558 commit 21285cc

File tree

3 files changed

+245
-44
lines changed

3 files changed

+245
-44
lines changed

google/cloud/policytroubleshooter_v1/services/iam_checker/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
@@ -101,6 +101,42 @@ def from_service_account_file(cls, filename: str, *args, **kwargs):
101101

102102
from_service_account_json = from_service_account_file
103103

104+
@classmethod
105+
def get_mtls_endpoint_and_cert_source(
106+
cls, client_options: Optional[ClientOptions] = None
107+
):
108+
"""Return the API endpoint and client cert source for mutual TLS.
109+
110+
The client cert source is determined in the following order:
111+
(1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the
112+
client cert source is None.
113+
(2) if `client_options.client_cert_source` is provided, use the provided one; if the
114+
default client cert source exists, use the default one; otherwise the client cert
115+
source is None.
116+
117+
The API endpoint is determined in the following order:
118+
(1) if `client_options.api_endpoint` if provided, use the provided one.
119+
(2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
120+
default mTLS endpoint; if the environment variabel is "never", use the default API
121+
endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
122+
use the default API endpoint.
123+
124+
More details can be found at https://google.aip.dev/auth/4114.
125+
126+
Args:
127+
client_options (google.api_core.client_options.ClientOptions): Custom options for the
128+
client. Only the `api_endpoint` and `client_cert_source` properties may be used
129+
in this method.
130+
131+
Returns:
132+
Tuple[str, Callable[[], Tuple[bytes, bytes]]]: returns the API endpoint and the
133+
client cert source to use.
134+
135+
Raises:
136+
google.auth.exceptions.MutualTLSChannelError: If any errors happen.
137+
"""
138+
return IamCheckerClient.get_mtls_endpoint_and_cert_source(client_options) # type: ignore
139+
104140
@property
105141
def transport(self) -> IamCheckerTransport:
106142
"""Returns the transport used by the client instance.

google/cloud/policytroubleshooter_v1/services/iam_checker/client.py

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

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

272-
# Create SSL credentials for mutual TLS if needed.
273-
if os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") not in (
274-
"true",
275-
"false",
276-
):
277-
raise ValueError(
278-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
279-
)
280-
use_client_cert = (
281-
os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true"
339+
api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source(
340+
client_options
282341
)
283342

284-
client_cert_source_func = None
285-
is_mtls = False
286-
if use_client_cert:
287-
if client_options.client_cert_source:
288-
is_mtls = True
289-
client_cert_source_func = client_options.client_cert_source
290-
else:
291-
is_mtls = mtls.has_default_client_cert_source()
292-
if is_mtls:
293-
client_cert_source_func = mtls.default_client_cert_source()
294-
else:
295-
client_cert_source_func = None
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-
else:
301-
use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
302-
if use_mtls_env == "never":
303-
api_endpoint = self.DEFAULT_ENDPOINT
304-
elif use_mtls_env == "always":
305-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
306-
elif use_mtls_env == "auto":
307-
if is_mtls:
308-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
309-
else:
310-
api_endpoint = self.DEFAULT_ENDPOINT
311-
else:
312-
raise MutualTLSChannelError(
313-
"Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted "
314-
"values: never, auto, always"
315-
)
343+
api_key_value = getattr(client_options, "api_key", None)
344+
if api_key_value and credentials:
345+
raise ValueError(
346+
"client_options.api_key and credentials are mutually exclusive"
347+
)
316348

317349
# Save or instantiate the transport.
318350
# Ordinarily, we provide the transport, but allowing a custom transport
319351
# instance provides an extensibility point for unusual situations.
320352
if isinstance(transport, IamCheckerTransport):
321353
# transport is a IamCheckerTransport instance.
322-
if credentials or client_options.credentials_file:
354+
if credentials or client_options.credentials_file or api_key_value:
323355
raise ValueError(
324356
"When providing a transport instance, "
325357
"provide its credentials directly."
@@ -331,6 +363,15 @@ def __init__(
331363
)
332364
self._transport = transport
333365
else:
366+
import google.auth._default # type: ignore
367+
368+
if api_key_value and hasattr(
369+
google.auth._default, "get_api_key_credentials"
370+
):
371+
credentials = google.auth._default.get_api_key_credentials(
372+
api_key_value
373+
)
374+
334375
Transport = type(self).get_transport_class(transport)
335376
self._transport = Transport(
336377
credentials=credentials,

tests/unit/gapic/policytroubleshooter_v1/test_iam_checker.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,83 @@ def test_iam_checker_client_mtls_env_auto(
386386
)
387387

388388

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

652+
# It is an error to provide an api_key and a transport instance.
653+
transport = transports.IamCheckerGrpcTransport(
654+
credentials=ga_credentials.AnonymousCredentials(),
655+
)
656+
options = client_options.ClientOptions()
657+
options.api_key = "api_key"
658+
with pytest.raises(ValueError):
659+
client = IamCheckerClient(client_options=options, transport=transport,)
660+
661+
# It is an error to provide an api_key and a credential.
662+
options = mock.Mock()
663+
options.api_key = "api_key"
664+
with pytest.raises(ValueError):
665+
client = IamCheckerClient(
666+
client_options=options, credentials=ga_credentials.AnonymousCredentials()
667+
)
668+
575669
# It is an error to provide scopes and a transport instance.
576670
transport = transports.IamCheckerGrpcTransport(
577671
credentials=ga_credentials.AnonymousCredentials(),
@@ -1088,3 +1182,33 @@ def test_client_ctx():
10881182
with client:
10891183
pass
10901184
close.assert_called()
1185+
1186+
1187+
@pytest.mark.parametrize(
1188+
"client_class,transport_class",
1189+
[
1190+
(IamCheckerClient, transports.IamCheckerGrpcTransport),
1191+
(IamCheckerAsyncClient, transports.IamCheckerGrpcAsyncIOTransport),
1192+
],
1193+
)
1194+
def test_api_key_credentials(client_class, transport_class):
1195+
with mock.patch.object(
1196+
google.auth._default, "get_api_key_credentials", create=True
1197+
) as get_api_key_credentials:
1198+
mock_cred = mock.Mock()
1199+
get_api_key_credentials.return_value = mock_cred
1200+
options = client_options.ClientOptions()
1201+
options.api_key = "api_key"
1202+
with mock.patch.object(transport_class, "__init__") as patched:
1203+
patched.return_value = None
1204+
client = client_class(client_options=options)
1205+
patched.assert_called_once_with(
1206+
credentials=mock_cred,
1207+
credentials_file=None,
1208+
host=client.DEFAULT_ENDPOINT,
1209+
scopes=None,
1210+
client_cert_source_for_mtls=None,
1211+
quota_project_id=None,
1212+
client_info=transports.base.DEFAULT_CLIENT_INFO,
1213+
always_use_jwt_access=True,
1214+
)

0 commit comments

Comments
 (0)