Skip to content

Commit 8d15f69

Browse files
ScruffyProdigylsiracclundin25
authored
fix: refactor credential subclass parameters (#1095)
* fix: refactor credential subclass parameters * Changes requested by @clundin25 * hotlinking __init__ method * punctuation * getting unit tests to pass * fixing comments * Refresh system test token. Co-authored-by: Leo <39062083+lsirac@users.noreply.github.com> Co-authored-by: Carl Lundin <clundin@google.com>
1 parent b2a901b commit 8d15f69

File tree

6 files changed

+81
-149
lines changed

6 files changed

+81
-149
lines changed

google/auth/aws.py

Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939

4040
import hashlib
4141
import hmac
42-
import io
4342
import json
4443
import os
4544
import posixpath
@@ -352,13 +351,8 @@ def __init__(
352351
subject_token_type,
353352
token_url,
354353
credential_source=None,
355-
service_account_impersonation_url=None,
356-
service_account_impersonation_options={},
357-
client_id=None,
358-
client_secret=None,
359-
quota_project_id=None,
360-
scopes=None,
361-
default_scopes=None,
354+
*args,
355+
**kwargs
362356
):
363357
"""Instantiates an AWS workload external account credentials object.
364358
@@ -369,15 +363,8 @@ def __init__(
369363
credential_source (Mapping): The credential source dictionary used
370364
to provide instructions on how to retrieve external credential
371365
to be exchanged for Google access tokens.
372-
service_account_impersonation_url (Optional[str]): The optional
373-
service account impersonation getAccessToken URL.
374-
client_id (Optional[str]): The optional client ID.
375-
client_secret (Optional[str]): The optional client secret.
376-
quota_project_id (Optional[str]): The optional quota project ID.
377-
scopes (Optional[Sequence[str]]): Optional scopes to request during
378-
the authorization grant.
379-
default_scopes (Optional[Sequence[str]]): Default scopes passed by a
380-
Google client library. Use 'scopes' for user-defined scopes.
366+
args (List): Optional positional arguments passed into the underlying :meth:`~external_account.Credentials.__init__` method.
367+
kwargs (Mapping): Optional keyword arguments passed into the underlying :meth:`~external_account.Credentials.__init__` method.
381368
382369
Raises:
383370
google.auth.exceptions.RefreshError: If an error is encountered during
@@ -393,13 +380,8 @@ def __init__(
393380
subject_token_type=subject_token_type,
394381
token_url=token_url,
395382
credential_source=credential_source,
396-
service_account_impersonation_url=service_account_impersonation_url,
397-
service_account_impersonation_options=service_account_impersonation_options,
398-
client_id=client_id,
399-
client_secret=client_secret,
400-
quota_project_id=quota_project_id,
401-
scopes=scopes,
402-
default_scopes=default_scopes,
383+
*args,
384+
**kwargs
403385
)
404386
credential_source = credential_source or {}
405387
self._environment_id = credential_source.get("environment_id") or ""
@@ -750,23 +732,7 @@ def from_info(cls, info, **kwargs):
750732
Raises:
751733
ValueError: For invalid parameters.
752734
"""
753-
return cls(
754-
audience=info.get("audience"),
755-
subject_token_type=info.get("subject_token_type"),
756-
token_url=info.get("token_url"),
757-
service_account_impersonation_url=info.get(
758-
"service_account_impersonation_url"
759-
),
760-
service_account_impersonation_options=info.get(
761-
"service_account_impersonation"
762-
)
763-
or {},
764-
client_id=info.get("client_id"),
765-
client_secret=info.get("client_secret"),
766-
credential_source=info.get("credential_source"),
767-
quota_project_id=info.get("quota_project_id"),
768-
**kwargs
769-
)
735+
return super(Credentials, cls).from_info(info, **kwargs)
770736

771737
@classmethod
772738
def from_file(cls, filename, **kwargs):
@@ -779,6 +745,4 @@ def from_file(cls, filename, **kwargs):
779745
Returns:
780746
google.auth.aws.Credentials: The constructed credentials.
781747
"""
782-
with io.open(filename, "r", encoding="utf-8") as json_file:
783-
data = json.load(json_file)
784-
return cls.from_info(data, **kwargs)
748+
return super(Credentials, cls).from_file(filename, **kwargs)

google/auth/external_account.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import abc
3131
import copy
3232
import datetime
33+
import io
3334
import json
3435
import re
3536

@@ -70,7 +71,7 @@ def __init__(
7071
token_url,
7172
credential_source,
7273
service_account_impersonation_url=None,
73-
service_account_impersonation_options={},
74+
service_account_impersonation_options=None,
7475
client_id=None,
7576
client_secret=None,
7677
quota_project_id=None,
@@ -482,3 +483,54 @@ def is_valid_url(patterns, url):
482483
return False
483484

484485
return any(re.compile(p).match(uri.hostname.lower()) for p in patterns)
486+
487+
@classmethod
488+
def from_info(cls, info, **kwargs):
489+
"""Creates a Credentials instance from parsed external account info.
490+
491+
Args:
492+
info (Mapping[str, str]): The external account info in Google
493+
format.
494+
kwargs: Additional arguments to pass to the constructor.
495+
496+
Returns:
497+
google.auth.identity_pool.Credentials: The constructed
498+
credentials.
499+
500+
Raises:
501+
ValueError: For invalid parameters.
502+
"""
503+
return cls(
504+
audience=info.get("audience"),
505+
subject_token_type=info.get("subject_token_type"),
506+
token_url=info.get("token_url"),
507+
service_account_impersonation_url=info.get(
508+
"service_account_impersonation_url"
509+
),
510+
service_account_impersonation_options=info.get(
511+
"service_account_impersonation"
512+
)
513+
or {},
514+
client_id=info.get("client_id"),
515+
client_secret=info.get("client_secret"),
516+
credential_source=info.get("credential_source"),
517+
quota_project_id=info.get("quota_project_id"),
518+
workforce_pool_user_project=info.get("workforce_pool_user_project"),
519+
**kwargs
520+
)
521+
522+
@classmethod
523+
def from_file(cls, filename, **kwargs):
524+
"""Creates a Credentials instance from an external account json file.
525+
526+
Args:
527+
filename (str): The path to the external account json file.
528+
kwargs: Additional arguments to pass to the constructor.
529+
530+
Returns:
531+
google.auth.identity_pool.Credentials: The constructed
532+
credentials.
533+
"""
534+
with io.open(filename, "r", encoding="utf-8") as json_file:
535+
data = json.load(json_file)
536+
return cls.from_info(data, **kwargs)

google/auth/identity_pool.py

Lines changed: 8 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,8 @@ def __init__(
5656
subject_token_type,
5757
token_url,
5858
credential_source,
59-
service_account_impersonation_url=None,
60-
service_account_impersonation_options={},
61-
client_id=None,
62-
client_secret=None,
63-
quota_project_id=None,
64-
scopes=None,
65-
default_scopes=None,
66-
workforce_pool_user_project=None,
59+
*args,
60+
**kwargs
6761
):
6862
"""Instantiates an external account credentials object from a file/URL.
6963
@@ -91,21 +85,8 @@ def __init__(
9185
{
9286
"file": "/path/to/token/file.txt"
9387
}
94-
95-
service_account_impersonation_url (Optional[str]): The optional service account
96-
impersonation getAccessToken URL.
97-
client_id (Optional[str]): The optional client ID.
98-
client_secret (Optional[str]): The optional client secret.
99-
quota_project_id (Optional[str]): The optional quota project ID.
100-
scopes (Optional[Sequence[str]]): Optional scopes to request during the
101-
authorization grant.
102-
default_scopes (Optional[Sequence[str]]): Default scopes passed by a
103-
Google client library. Use 'scopes' for user-defined scopes.
104-
workforce_pool_user_project (Optona[str]): The optional workforce pool user
105-
project number when the credential corresponds to a workforce pool and not
106-
a workload identity pool. The underlying principal must still have
107-
serviceusage.services.use IAM permission to use the project for
108-
billing/quota.
88+
args (List): Optional positional arguments passed into the underlying :meth:`~external_account.Credentials.__init__` method.
89+
kwargs (Mapping): Optional keyword arguments passed into the underlying :meth:`~external_account.Credentials.__init__` method.
10990
11091
Raises:
11192
google.auth.exceptions.RefreshError: If an error is encountered during
@@ -122,14 +103,8 @@ def __init__(
122103
subject_token_type=subject_token_type,
123104
token_url=token_url,
124105
credential_source=credential_source,
125-
service_account_impersonation_url=service_account_impersonation_url,
126-
service_account_impersonation_options=service_account_impersonation_options,
127-
client_id=client_id,
128-
client_secret=client_secret,
129-
quota_project_id=quota_project_id,
130-
scopes=scopes,
131-
default_scopes=default_scopes,
132-
workforce_pool_user_project=workforce_pool_user_project,
106+
*args,
107+
**kwargs
133108
)
134109
if not isinstance(credential_source, Mapping):
135110
self._credential_source_file = None
@@ -257,24 +232,7 @@ def from_info(cls, info, **kwargs):
257232
Raises:
258233
ValueError: For invalid parameters.
259234
"""
260-
return cls(
261-
audience=info.get("audience"),
262-
subject_token_type=info.get("subject_token_type"),
263-
token_url=info.get("token_url"),
264-
service_account_impersonation_url=info.get(
265-
"service_account_impersonation_url"
266-
),
267-
service_account_impersonation_options=info.get(
268-
"service_account_impersonation"
269-
)
270-
or {},
271-
client_id=info.get("client_id"),
272-
client_secret=info.get("client_secret"),
273-
credential_source=info.get("credential_source"),
274-
quota_project_id=info.get("quota_project_id"),
275-
workforce_pool_user_project=info.get("workforce_pool_user_project"),
276-
**kwargs
277-
)
235+
return super(Credentials, cls).from_info(info, **kwargs)
278236

279237
@classmethod
280238
def from_file(cls, filename, **kwargs):
@@ -288,6 +246,4 @@ def from_file(cls, filename, **kwargs):
288246
google.auth.identity_pool.Credentials: The constructed
289247
credentials.
290248
"""
291-
with io.open(filename, "r", encoding="utf-8") as json_file:
292-
data = json.load(json_file)
293-
return cls.from_info(data, **kwargs)
249+
return super(Credentials, cls).from_file(filename, **kwargs)

google/auth/pluggable.py

Lines changed: 8 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
# Python 2.7 compatibility
3636
except ImportError: # pragma: NO COVER
3737
from collections import Mapping
38-
import io
3938
import json
4039
import os
4140
import subprocess
@@ -58,14 +57,8 @@ def __init__(
5857
subject_token_type,
5958
token_url,
6059
credential_source,
61-
service_account_impersonation_url=None,
62-
service_account_impersonation_options={},
63-
client_id=None,
64-
client_secret=None,
65-
quota_project_id=None,
66-
scopes=None,
67-
default_scopes=None,
68-
workforce_pool_user_project=None,
60+
*args,
61+
**kwargs
6962
):
7063
"""Instantiates an external account credentials object from a executables.
7164
@@ -86,21 +79,8 @@ def __init__(
8679
"output_file": "/path/to/generated/cached/credentials"
8780
}
8881
}
89-
90-
service_account_impersonation_url (Optional[str]): The optional service account
91-
impersonation getAccessToken URL.
92-
client_id (Optional[str]): The optional client ID.
93-
client_secret (Optional[str]): The optional client secret.
94-
quota_project_id (Optional[str]): The optional quota project ID.
95-
scopes (Optional[Sequence[str]]): Optional scopes to request during the
96-
authorization grant.
97-
default_scopes (Optional[Sequence[str]]): Default scopes passed by a
98-
Google client library. Use 'scopes' for user-defined scopes.
99-
workforce_pool_user_project (Optona[str]): The optional workforce pool user
100-
project number when the credential corresponds to a workforce pool and not
101-
a workload Pluggable. The underlying principal must still have
102-
serviceusage.services.use IAM permission to use the project for
103-
billing/quota.
82+
args (List): Optional positional arguments passed into the underlying :meth:`~external_account.Credentials.__init__` method.
83+
kwargs (Mapping): Optional keyword arguments passed into the underlying :meth:`~external_account.Credentials.__init__` method.
10484
10585
Raises:
10686
google.auth.exceptions.RefreshError: If an error is encountered during
@@ -117,13 +97,8 @@ def __init__(
11797
subject_token_type=subject_token_type,
11898
token_url=token_url,
11999
credential_source=credential_source,
120-
service_account_impersonation_url=service_account_impersonation_url,
121-
client_id=client_id,
122-
client_secret=client_secret,
123-
quota_project_id=quota_project_id,
124-
scopes=scopes,
125-
default_scopes=default_scopes,
126-
workforce_pool_user_project=workforce_pool_user_project,
100+
*args,
101+
**kwargs
127102
)
128103
if not isinstance(credential_source, Mapping):
129104
self._credential_source_executable = None
@@ -250,24 +225,7 @@ def from_info(cls, info, **kwargs):
250225
Raises:
251226
ValueError: For invalid parameters.
252227
"""
253-
return cls(
254-
audience=info.get("audience"),
255-
subject_token_type=info.get("subject_token_type"),
256-
token_url=info.get("token_url"),
257-
service_account_impersonation_url=info.get(
258-
"service_account_impersonation_url"
259-
),
260-
service_account_impersonation_options=info.get(
261-
"service_account_impersonation"
262-
)
263-
or {},
264-
client_id=info.get("client_id"),
265-
client_secret=info.get("client_secret"),
266-
credential_source=info.get("credential_source"),
267-
quota_project_id=info.get("quota_project_id"),
268-
workforce_pool_user_project=info.get("workforce_pool_user_project"),
269-
**kwargs
270-
)
228+
return super(Credentials, cls).from_info(info, **kwargs)
271229

272230
@classmethod
273231
def from_file(cls, filename, **kwargs):
@@ -281,9 +239,7 @@ def from_file(cls, filename, **kwargs):
281239
google.auth.pluggable.Credentials: The constructed
282240
credentials.
283241
"""
284-
with io.open(filename, "r", encoding="utf-8") as json_file:
285-
data = json.load(json_file)
286-
return cls.from_info(data, **kwargs)
242+
return super(Credentials, cls).from_file(filename, **kwargs)
287243

288244
def _parse_subject_token(self, response):
289245
if "version" not in response:

system_tests/secrets.tar.enc

0 Bytes
Binary file not shown.

tests/test_aws.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,7 @@ def test_from_info_full_options(self, mock_init):
817817
client_secret=CLIENT_SECRET,
818818
credential_source=self.CREDENTIAL_SOURCE,
819819
quota_project_id=QUOTA_PROJECT_ID,
820+
workforce_pool_user_project=None,
820821
)
821822

822823
@mock.patch.object(aws.Credentials, "__init__", return_value=None)
@@ -842,6 +843,7 @@ def test_from_info_required_options_only(self, mock_init):
842843
client_secret=None,
843844
credential_source=self.CREDENTIAL_SOURCE,
844845
quota_project_id=None,
846+
workforce_pool_user_project=None,
845847
)
846848

847849
@mock.patch.object(aws.Credentials, "__init__", return_value=None)
@@ -873,6 +875,7 @@ def test_from_file_full_options(self, mock_init, tmpdir):
873875
client_secret=CLIENT_SECRET,
874876
credential_source=self.CREDENTIAL_SOURCE,
875877
quota_project_id=QUOTA_PROJECT_ID,
878+
workforce_pool_user_project=None,
876879
)
877880

878881
@mock.patch.object(aws.Credentials, "__init__", return_value=None)
@@ -899,6 +902,7 @@ def test_from_file_required_options_only(self, mock_init, tmpdir):
899902
client_secret=None,
900903
credential_source=self.CREDENTIAL_SOURCE,
901904
quota_project_id=None,
905+
workforce_pool_user_project=None,
902906
)
903907

904908
def test_constructor_invalid_credential_source(self):

0 commit comments

Comments
 (0)