Skip to content

Commit 988153d

Browse files
authored
feat: Add optional account association for Authorized User credentials. (#1458)
* feat: Add optional account association for Authorized User credentials. * chore: Refresh system test creds. * Fix two missed constructors.
1 parent 9cd6742 commit 988153d

File tree

3 files changed

+49
-0
lines changed

3 files changed

+49
-0
lines changed

google/oauth2/credentials.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ def __init__(
8787
granted_scopes=None,
8888
trust_boundary=None,
8989
universe_domain=_DEFAULT_UNIVERSE_DOMAIN,
90+
account=None,
9091
):
9192
"""
9293
Args:
@@ -131,6 +132,7 @@ def __init__(
131132
trust_boundary (str): String representation of trust boundary meta.
132133
universe_domain (Optional[str]): The universe domain. The default
133134
universe domain is googleapis.com.
135+
account (Optional[str]): The account associated with the credential.
134136
"""
135137
super(Credentials, self).__init__()
136138
self.token = token
@@ -149,6 +151,7 @@ def __init__(
149151
self._enable_reauth_refresh = enable_reauth_refresh
150152
self._trust_boundary = trust_boundary
151153
self._universe_domain = universe_domain or _DEFAULT_UNIVERSE_DOMAIN
154+
self._account = account or ""
152155

153156
def __getstate__(self):
154157
"""A __getstate__ method must exist for the __setstate__ to be called
@@ -189,6 +192,7 @@ def __setstate__(self, d):
189192
self._refresh_handler = None
190193
self._refresh_worker = None
191194
self._use_non_blocking_refresh = d.get("_use_non_blocking_refresh", False)
195+
self._account = d.get("_account", "")
192196

193197
@property
194198
def refresh_token(self):
@@ -268,6 +272,11 @@ def refresh_handler(self, value):
268272
raise TypeError("The provided refresh_handler is not a callable or None.")
269273
self._refresh_handler = value
270274

275+
@property
276+
def account(self):
277+
"""str: The user account associated with the credential. If the account is unknown an empty string is returned."""
278+
return self._account
279+
271280
@_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
272281
def with_quota_project(self, quota_project_id):
273282

@@ -286,6 +295,7 @@ def with_quota_project(self, quota_project_id):
286295
enable_reauth_refresh=self._enable_reauth_refresh,
287296
trust_boundary=self._trust_boundary,
288297
universe_domain=self._universe_domain,
298+
account=self._account,
289299
)
290300

291301
@_helpers.copy_docstring(credentials.CredentialsWithTokenUri)
@@ -306,6 +316,35 @@ def with_token_uri(self, token_uri):
306316
enable_reauth_refresh=self._enable_reauth_refresh,
307317
trust_boundary=self._trust_boundary,
308318
universe_domain=self._universe_domain,
319+
account=self._account,
320+
)
321+
322+
def with_account(self, account):
323+
"""Returns a copy of these credentials with a modified account.
324+
325+
Args:
326+
account (str): The account to set
327+
328+
Returns:
329+
google.oauth2.credentials.Credentials: A new credentials instance.
330+
"""
331+
332+
return self.__class__(
333+
self.token,
334+
refresh_token=self.refresh_token,
335+
id_token=self.id_token,
336+
token_uri=self._token_uri,
337+
client_id=self.client_id,
338+
client_secret=self.client_secret,
339+
scopes=self.scopes,
340+
default_scopes=self.default_scopes,
341+
granted_scopes=self.granted_scopes,
342+
quota_project_id=self.quota_project_id,
343+
rapt_token=self.rapt_token,
344+
enable_reauth_refresh=self._enable_reauth_refresh,
345+
trust_boundary=self._trust_boundary,
346+
universe_domain=self._universe_domain,
347+
account=account,
309348
)
310349

311350
@_helpers.copy_docstring(credentials.CredentialsWithUniverseDomain)
@@ -326,6 +365,7 @@ def with_universe_domain(self, universe_domain):
326365
enable_reauth_refresh=self._enable_reauth_refresh,
327366
trust_boundary=self._trust_boundary,
328367
universe_domain=universe_domain,
368+
account=self._account,
329369
)
330370

331371
def _metric_header_for_usage(self):
@@ -474,6 +514,7 @@ def from_authorized_user_info(cls, info, scopes=None):
474514
rapt_token=info.get("rapt_token"), # may not exist
475515
trust_boundary=info.get("trust_boundary"), # may not exist
476516
universe_domain=info.get("universe_domain"), # may not exist
517+
account=info.get("account", ""), # may not exist
477518
)
478519

479520
@classmethod
@@ -518,6 +559,7 @@ def to_json(self, strip=None):
518559
"scopes": self.scopes,
519560
"rapt_token": self.rapt_token,
520561
"universe_domain": self._universe_domain,
562+
"account": self._account,
521563
}
522564
if self.expiry: # flatten expiry timestamp
523565
prep["expiry"] = self.expiry.isoformat() + "Z"

system_tests/secrets.tar.enc

0 Bytes
Binary file not shown.

tests/oauth2/test_credentials.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,12 @@ def test_with_universe_domain(self):
793793
new_creds = creds.with_universe_domain("dummy_universe.com")
794794
assert new_creds.universe_domain == "dummy_universe.com"
795795

796+
def test_with_account(self):
797+
creds = credentials.Credentials(token="token")
798+
assert creds.account == ""
799+
new_creds = creds.with_account("[email protected]")
800+
assert new_creds.account == "[email protected]"
801+
796802
def test_with_token_uri(self):
797803
info = AUTH_USER_INFO.copy()
798804

@@ -888,6 +894,7 @@ def test_to_json(self):
888894
assert json_asdict.get("client_secret") == creds.client_secret
889895
assert json_asdict.get("expiry") == info["expiry"]
890896
assert json_asdict.get("universe_domain") == creds.universe_domain
897+
assert json_asdict.get("account") == creds.account
891898

892899
# Test with a `strip` arg
893900
json_output = creds.to_json(strip=["client_secret"])

0 commit comments

Comments
 (0)