Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: send quota project id in x-goog-user-project for OAuth2 credentials #400

Merged
merged 4 commits into from
Dec 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/reference/google.auth.crypt.base.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
google.auth.crypt.base module
=============================

.. automodule:: google.auth.crypt.base
:members:
:inherited-members:
:show-inheritance:
7 changes: 7 additions & 0 deletions docs/reference/google.auth.crypt.rsa.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
google.auth.crypt.rsa module
============================

.. automodule:: google.auth.crypt.rsa
:members:
:inherited-members:
:show-inheritance:
19 changes: 19 additions & 0 deletions google/oauth2/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def __init__(
client_id=None,
client_secret=None,
scopes=None,
quota_project_id=None,
):
"""
Args:
Expand All @@ -81,6 +82,9 @@ def __init__(
token if refresh information is provided (e.g. The refresh
token scopes are a superset of this or contain a wild card
scope like 'https://www.googleapis.com/auth/any-api').
quota_project_id (Optional[str]): The project ID used for quota and billing.
This project may be different from the project used to
create the credentials.
"""
super(Credentials, self).__init__()
self.token = token
Expand All @@ -90,6 +94,7 @@ def __init__(
self._token_uri = token_uri
self._client_id = client_id
self._client_secret = client_secret
self._quota_project_id = quota_project_id

@property
def refresh_token(self):
Expand Down Expand Up @@ -123,6 +128,11 @@ def client_secret(self):
"""Optional[str]: The OAuth 2.0 client secret."""
return self._client_secret

@property
def quota_project_id(self):
"""Optional[str]: The project to use for quota and billing purposes."""
return self._quota_project_id

@property
def requires_scopes(self):
"""False: OAuth 2.0 credentials have their scopes set when
Expand Down Expand Up @@ -169,6 +179,12 @@ def refresh(self, request):
)
)

@_helpers.copy_docstring(credentials.Credentials)
def apply(self, headers, token=None):
super(Credentials, self).apply(headers, token=token)
if self.quota_project_id is not None:
headers["x-goog-user-project"] = self.quota_project_id

@classmethod
def from_authorized_user_info(cls, info, scopes=None):
"""Creates a Credentials instance from parsed authorized user info.
Expand Down Expand Up @@ -202,6 +218,9 @@ def from_authorized_user_info(cls, info, scopes=None):
scopes=scopes,
client_id=info["client_id"],
client_secret=info["client_secret"],
quota_project_id=info.get(
"quota_project_id"
), # quota project may not exist
)

@classmethod
Expand Down
27 changes: 27 additions & 0 deletions tests/oauth2/test_credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,33 @@ def test_credentials_with_scopes_refresh_failure_raises_refresh_error(
# expired.)
assert creds.valid

def test_apply_with_quota_project_id(self):
creds = credentials.Credentials(
token="token",
refresh_token=self.REFRESH_TOKEN,
token_uri=self.TOKEN_URI,
client_id=self.CLIENT_ID,
client_secret=self.CLIENT_SECRET,
quota_project_id="quota-project-123",
)

headers = {}
creds.apply(headers)
assert headers["x-goog-user-project"] == "quota-project-123"

def test_apply_with_no_quota_project_id(self):
creds = credentials.Credentials(
token="token",
refresh_token=self.REFRESH_TOKEN,
token_uri=self.TOKEN_URI,
client_id=self.CLIENT_ID,
client_secret=self.CLIENT_SECRET,
)

headers = {}
creds.apply(headers)
assert "x-goog-user-project" not in headers

def test_from_authorized_user_info(self):
info = AUTH_USER_INFO.copy()

Expand Down