From 0a226948d91572acf784ffb8bc24c6d45c3e269b Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 22 Nov 2019 16:31:33 -0800 Subject: [PATCH 1/4] feat: pass x-goog-user-project when quota_project is set --- google/oauth2/credentials.py | 17 +++++++++++++++++ tests/data/authorized_user.json | 3 ++- tests/oauth2/test_credentials.py | 4 ++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/google/oauth2/credentials.py b/google/oauth2/credentials.py index 676a4324e..f6a825316 100644 --- a/google/oauth2/credentials.py +++ b/google/oauth2/credentials.py @@ -58,6 +58,7 @@ def __init__( client_id=None, client_secret=None, scopes=None, + quota_project=None, ): """ Args: @@ -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 (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 @@ -90,6 +94,7 @@ def __init__( self._token_uri = token_uri self._client_id = client_id self._client_secret = client_secret + self._quota_project = quota_project @property def refresh_token(self): @@ -123,6 +128,11 @@ def client_secret(self): """Optional[str]: The OAuth 2.0 client secret.""" return self._client_secret + @property + def quota_project(self): + """Optional[str]: The project to use for quota and billing purposes.""" + return self._quota_project + @property def requires_scopes(self): """False: OAuth 2.0 credentials have their scopes set when @@ -169,6 +179,12 @@ def refresh(self, request): ) ) + @_helpers.copy_docstring(credentials.Credentials) + def apply(self, headers, token=None): + super().apply(headers, token=token) + if self.quota_project is not None: + headers["x-goog-user-project"] = self.quota_project + @classmethod def from_authorized_user_info(cls, info, scopes=None): """Creates a Credentials instance from parsed authorized user info. @@ -202,6 +218,7 @@ def from_authorized_user_info(cls, info, scopes=None): scopes=scopes, client_id=info["client_id"], client_secret=info["client_secret"], + quota_project=info.get("quota_project"), # quota project may not exist ) @classmethod diff --git a/tests/data/authorized_user.json b/tests/data/authorized_user.json index 4787acee5..0d116d59b 100644 --- a/tests/data/authorized_user.json +++ b/tests/data/authorized_user.json @@ -2,5 +2,6 @@ "client_id": "123", "client_secret": "secret", "refresh_token": "alabalaportocala", - "type": "authorized_user" + "type": "authorized_user", + "quota_project": "offiredulassed" } diff --git a/tests/oauth2/test_credentials.py b/tests/oauth2/test_credentials.py index 8bfdd7e0a..3f7343b1b 100644 --- a/tests/oauth2/test_credentials.py +++ b/tests/oauth2/test_credentials.py @@ -303,6 +303,7 @@ def test_from_authorized_user_info(self): assert creds.refresh_token == info["refresh_token"] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes is None + assert creds.quota_project = info["quota_project"] scopes = ["email", "profile"] creds = credentials.Credentials.from_authorized_user_info(info, scopes) @@ -311,6 +312,7 @@ def test_from_authorized_user_info(self): assert creds.refresh_token == info["refresh_token"] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes == scopes + assert creds.quota_project = info["quota_project"] def test_from_authorized_user_file(self): info = AUTH_USER_INFO.copy() @@ -321,6 +323,7 @@ def test_from_authorized_user_file(self): assert creds.refresh_token == info["refresh_token"] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes is None + assert creds.quota_project = info["quota_project"] scopes = ["email", "profile"] creds = credentials.Credentials.from_authorized_user_file( @@ -331,3 +334,4 @@ def test_from_authorized_user_file(self): assert creds.refresh_token == info["refresh_token"] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes == scopes + assert creds.quota_project = info["quota_project"] From 7bf1d1c34bcba9b9066afce02eb09778f66be52e Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 5 Dec 2019 14:49:59 -0800 Subject: [PATCH 2/4] test: add unit tests, make compatible with Python 2 --- docs/reference/google.auth.crypt.base.rst | 7 +++++ docs/reference/google.auth.crypt.rsa.rst | 7 +++++ google/oauth2/credentials.py | 2 +- tests/oauth2/test_credentials.py | 31 ++++++++++++++++++++--- 4 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 docs/reference/google.auth.crypt.base.rst create mode 100644 docs/reference/google.auth.crypt.rsa.rst diff --git a/docs/reference/google.auth.crypt.base.rst b/docs/reference/google.auth.crypt.base.rst new file mode 100644 index 000000000..a8996501a --- /dev/null +++ b/docs/reference/google.auth.crypt.base.rst @@ -0,0 +1,7 @@ +google.auth.crypt.base module +============================= + +.. automodule:: google.auth.crypt.base + :members: + :inherited-members: + :show-inheritance: diff --git a/docs/reference/google.auth.crypt.rsa.rst b/docs/reference/google.auth.crypt.rsa.rst new file mode 100644 index 000000000..7060b03c8 --- /dev/null +++ b/docs/reference/google.auth.crypt.rsa.rst @@ -0,0 +1,7 @@ +google.auth.crypt.rsa module +============================ + +.. automodule:: google.auth.crypt.rsa + :members: + :inherited-members: + :show-inheritance: diff --git a/google/oauth2/credentials.py b/google/oauth2/credentials.py index f6a825316..6c7b7286c 100644 --- a/google/oauth2/credentials.py +++ b/google/oauth2/credentials.py @@ -181,7 +181,7 @@ def refresh(self, request): @_helpers.copy_docstring(credentials.Credentials) def apply(self, headers, token=None): - super().apply(headers, token=token) + super(Credentials, self).apply(headers, token=token) if self.quota_project is not None: headers["x-goog-user-project"] = self.quota_project diff --git a/tests/oauth2/test_credentials.py b/tests/oauth2/test_credentials.py index 3f7343b1b..096d7a8eb 100644 --- a/tests/oauth2/test_credentials.py +++ b/tests/oauth2/test_credentials.py @@ -294,6 +294,33 @@ def test_credentials_with_scopes_refresh_failure_raises_refresh_error( # expired.) assert creds.valid + def test_apply_with_quota_project(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="quota-project-123", + ) + + headers = {} + creds.apply(headers) + assert headers["x-goog-user-project"] == "quota-project-123" + + def test_apply_with_no_quota_project(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() @@ -303,7 +330,6 @@ def test_from_authorized_user_info(self): assert creds.refresh_token == info["refresh_token"] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes is None - assert creds.quota_project = info["quota_project"] scopes = ["email", "profile"] creds = credentials.Credentials.from_authorized_user_info(info, scopes) @@ -312,7 +338,6 @@ def test_from_authorized_user_info(self): assert creds.refresh_token == info["refresh_token"] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes == scopes - assert creds.quota_project = info["quota_project"] def test_from_authorized_user_file(self): info = AUTH_USER_INFO.copy() @@ -323,7 +348,6 @@ def test_from_authorized_user_file(self): assert creds.refresh_token == info["refresh_token"] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes is None - assert creds.quota_project = info["quota_project"] scopes = ["email", "profile"] creds = credentials.Credentials.from_authorized_user_file( @@ -334,4 +358,3 @@ def test_from_authorized_user_file(self): assert creds.refresh_token == info["refresh_token"] assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT assert creds.scopes == scopes - assert creds.quota_project = info["quota_project"] From 1af311fc5332aba0b74de6c47c6f255fe23ea3c4 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 5 Dec 2019 14:52:38 -0800 Subject: [PATCH 3/4] chore: remove quota_project from sample authorized user file --- tests/data/authorized_user.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/data/authorized_user.json b/tests/data/authorized_user.json index 0d116d59b..4787acee5 100644 --- a/tests/data/authorized_user.json +++ b/tests/data/authorized_user.json @@ -2,6 +2,5 @@ "client_id": "123", "client_secret": "secret", "refresh_token": "alabalaportocala", - "type": "authorized_user", - "quota_project": "offiredulassed" + "type": "authorized_user" } From ac2deffebce8c6e227a58f2b0b177e4df49e2abf Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 5 Dec 2019 15:08:09 -0800 Subject: [PATCH 4/4] fix: quota_project -> quota_project_id --- google/oauth2/credentials.py | 18 ++++++++++-------- tests/oauth2/test_credentials.py | 6 +++--- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/google/oauth2/credentials.py b/google/oauth2/credentials.py index 6c7b7286c..2c7acf6c8 100644 --- a/google/oauth2/credentials.py +++ b/google/oauth2/credentials.py @@ -58,7 +58,7 @@ def __init__( client_id=None, client_secret=None, scopes=None, - quota_project=None, + quota_project_id=None, ): """ Args: @@ -82,7 +82,7 @@ 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 (Optional[str]): The project ID used for quota and billing. + 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. """ @@ -94,7 +94,7 @@ def __init__( self._token_uri = token_uri self._client_id = client_id self._client_secret = client_secret - self._quota_project = quota_project + self._quota_project_id = quota_project_id @property def refresh_token(self): @@ -129,9 +129,9 @@ def client_secret(self): return self._client_secret @property - def quota_project(self): + def quota_project_id(self): """Optional[str]: The project to use for quota and billing purposes.""" - return self._quota_project + return self._quota_project_id @property def requires_scopes(self): @@ -182,8 +182,8 @@ 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 is not None: - headers["x-goog-user-project"] = self.quota_project + 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): @@ -218,7 +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=info.get("quota_project"), # quota project may not exist + quota_project_id=info.get( + "quota_project_id" + ), # quota project may not exist ) @classmethod diff --git a/tests/oauth2/test_credentials.py b/tests/oauth2/test_credentials.py index 096d7a8eb..0b5993462 100644 --- a/tests/oauth2/test_credentials.py +++ b/tests/oauth2/test_credentials.py @@ -294,21 +294,21 @@ def test_credentials_with_scopes_refresh_failure_raises_refresh_error( # expired.) assert creds.valid - def test_apply_with_quota_project(self): + 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="quota-project-123", + 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(self): + def test_apply_with_no_quota_project_id(self): creds = credentials.Credentials( token="token", refresh_token=self.REFRESH_TOKEN,