8000 feat: add api key support (#144) · googleapis/google-cloud-python@69675b6 · GitHub
[go: up one dir, main page]

Skip to content

Commit 69675b6

Browse files
feat: add api key support (#144)
* chore: upgrade gapic-generator-java, gax-java and gapic-generator-python PiperOrigin-RevId: 423842556 Source-Link: googleapis/googleapis@a616ca0 Source-Link: googleapis/googleapis-gen@29b938c 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 c6d309e commit 69675b6

File tree

3 files changed

+249
-44
lines changed

3 files changed

+249
-44
lines changed

packages/google-cloud-os-login/google/cloud/oslogin_v1/services/os_login_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
@@ -117,6 +117,42 @@ def from_service_account_file(cls, filename: str, *args, **kwargs):
117117

118118
from_service_account_json = from_service_account_file
119119

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

packages/google-cloud-os-login/google/cloud/oslogin_v1/services/os_login_service/client.py

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

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

300-
# Create SSL credentials for mutual TLS if needed.
301-
if os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") not in (
302-
"true",
303-
"false",
304-
):
305-
raise ValueError(
306-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
307-
)
308-
use_client_cert = (
309-
os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true"
367+
api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source(
368+
client_options
310369
)
311370

312-
client_cert_source_func = None
313-
is_mtls = False
314-
if use_client_cert:
315-
if client_options.client_cert_source:
316-
is_mtls = True
317-
client_cert_source_func = client_options.client_cert_source
318-
else:
319-
is_mtls = mtls.has_default_client_cert_source()
320-
if is_mtls:
321-
client_cert_source_func = mtls.default_client_cert_source()
322-
else:
323-
client_cert_source_func = None
324-
325-
# Figure out which api endpoint to use.
326-
if client_options.api_endpoint is not None:
327-
api_endpoint = client_options.api_endpoint
328-
else:
329-
use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
330-
if use_mtls_env == "never":
331-
api_endpoint = self.DEFAULT_ENDPOINT
332-
elif use_mtls_env == "always":
333-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
334-
elif use_mtls_env == "auto":
335-
if is_mtls:
336-
api_endpoint = self.DEFAULT_MTLS_ENDPOINT
337-
else:
338-
api_endpoint = self.DEFAULT_ENDPOINT
339-
else:
340-
raise MutualTLSChannelError(
341-
"Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted "
342-
"values: never, auto, always"
343-
)
371+
api_key_value = getattr(client_options, "api_key", None)
372+
if api_key_value and credentials:
373+
raise ValueError(
374+
"client_options.api_key and credentials are mutually exclusive"
375+
)
344376

345377
# Save or instantiate the transport.
346378
# Ordinarily, we provide the transport, but allowing a custom transport
347379
# instance provides an extensibility point for unusual situations.
348380
if isinstance(transport, OsLoginServiceTransport):
349381
# transport is a OsLoginServiceTransport instance.
350-
if credentials or client_options.credentials_file:
382+
if credentials or client_options.credentials_file or api_key_value:
351383
raise ValueError(
352384
"When providing a transport instance, "
353385
"provide its credentials directly."
@@ -359,6 +391,15 @@ def __init__(
359391
)
360392
self._transport = transport
361393
else:
394+
import google.auth._default # type: ignore
395+
396+
if api_key_value and hasattr(
397+
google.auth._default, "get_api_key_credentials"
398+
):
399+
credentials = google.auth._default.get_api_key_credentials(
400+
api_key_value
401+
)
402+
362403
Transport = type(self).get_transport_class(transport)
363404
self._transport = Transport(
364405
credentials=credentials,

packages/google-cloud-os-login/tests/unit/gapic/oslogin_v1/test_os_login_service.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,87 @@ def test_os_login_service_client_mtls_env_auto(
396396
)
397397

398398

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

1930+
# It is an error to provide an api_key and a transport instance.
1931+
transport = transports.OsLoginServiceGrpcTransport(
1932+
credentials=ga_credentials.AnonymousCredentials(),
1933+
)
1934+
options = client_options.ClientOptions()
1935+
options.api_key = "api_key"
1936+
with pytest.raises(ValueError):
1937+
client = OsLoginServiceClient(client_options=options, transport=transport,)
1938+
1939+
# It is an error to provide an api_key and a credential.
1940+
options = mock.Mock()
1941+
options.api_key = "api_key"
1942+
with pytest.raises(ValueError):
1943+
client = OsLoginServiceClient(
1944+
client_options=options, credentials=ga_credentials.AnonymousCredentials()
1945+
)
1946+
18491947
# It is an error to provide scopes and a transport instance.
18501948
transport = transports.OsLoginServiceGrpcTransport(
18511949
credentials=ga_credentials.AnonymousCredentials(),
@@ -2440,3 +2538,33 @@ def test_client_ctx():
24402538
with client:
24412539
pass
24422540
close.assert_called()
2541+
2542+
2543+
@pytest.mark.parametrize(
2544+
"client_class,transport_class",
2545+
[
2546+
(OsLoginServiceClient, transports.OsLoginServiceGrpcTransport),
2547+
(OsLoginServiceAsyncClient, transports.OsLoginServiceGrpcAsyncIOTransport),
2548+
],
2549+
)
2550+
def test_api_key_credentials(client_class, transport_class):
2551+
with mock.patch.object(
2552+
google.auth._default, "get_api_key_credentials", create=True
2553+
) as get_api_key_credentials:
2554+
mock_cred = mock.Mock()
2555+
get_api_key_credentials.return_value = mock_cred
2556+
options = client_options.ClientOptions()
2557+
options.api_key = "api_key"
2558+
with mock.patch.object(transport_class, "__init__") as patched:
2559+
patched.return_value = None
2560+
client = client_class(client_options=options)
2561+
patched.assert_called_once_with(
2562+
credentials=mock_cred,
2563+
credentials_file=None,
2564+
host=client.DEFAULT_ENDPOINT,
2565+
scopes=None,
2566+
client_cert_source_for_mtls=None,
2567+
quota_project_id=None,
2568+
client_info=transports.base.DEFAULT_CLIENT_INFO,
2569+
always_use_jwt_access=True,
2570+
)

0 commit comments

Comments
 (0)
0