From 774906e9445fc54caec47f14c252e9dfdf9321dd Mon Sep 17 00:00:00 2001 From: David Harcombe Date: Thu, 7 Sep 2023 14:25:16 +0000 Subject: [PATCH 1/2] Updaate to remove legacy style support --- auth/credentials.py | 31 ++++++++++--------------------- pyproject.toml | 2 +- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/auth/credentials.py b/auth/credentials.py index 2ea827d..a4ae185 100644 --- a/auth/credentials.py +++ b/auth/credentials.py @@ -15,8 +15,7 @@ from dataclasses import dataclass from datetime import datetime -import json -from typing import Any, Dict, Type, TypeVar +from typing import Any, Dict, Mapping, Type, TypeVar, Union import pytz from dateutil.relativedelta import relativedelta @@ -24,9 +23,9 @@ from google.oauth2 import credentials as oauth from auth import decorators -from .credentials_helpers import encode_key from .abstract_datastore import AbstractDatastore +from .credentials_helpers import encode_key from .exceptions import CredentialsError @@ -36,7 +35,6 @@ class ProjectCredentials(object): client_secret: str - class Credentials(object): """Credentials. @@ -97,7 +95,7 @@ def token_details(self) -> Dict[str, Any]: return self.datastore.get_document(id=encode_key(self._email)) def store_credentials(self, - creds: oauth.Credentials) -> None: + creds: Union[oauth.Credentials, Mapping[str, Any]]) -> None: """Stores the credentials. This function uses the datastore to store the user credentials for later. @@ -110,8 +108,11 @@ def store_credentials(self, """ if self._email: key = encode_key(self._email) - json_creds = json.loads(creds.to_json()) - self.datastore.update_document(id=key, new_data=json_creds) + + if isinstance(creds, oauth.Credentials): + self.datastore.update_document(id=key, new_data=creds.to_json()) + else: + self.datastore.update_document(id=key, new_data=creds) def _refresh_credentials(self, creds: oauth.Credentials) -> None: """Refreshes the Google OAuth credentials. @@ -139,18 +140,7 @@ def credentials(self) -> oauth.Credentials: expiry = self._to_utc( datetime.now().astimezone(pytz.utc) + relativedelta(minutes=30)) if token := self.token_details: - if token.get('access_token'): - # This handles old-style credential storages. - creds = oauth.Credentials.from_authorized_user_info({ - 'token': token['access_token'], - 'refresh_token': token['refresh_token'], - 'client_id': self.project_credentials.client_id, - 'client_secret': self.project_credentials.client_secret, - }) - - else: - creds = \ - oauth.Credentials.from_authorized_user_info(token) + creds = oauth.Credentials.from_authorized_user_info(token) if creds.expired: creds.expiry = expiry @@ -158,8 +148,7 @@ def credentials(self) -> oauth.Credentials: else: creds = None - raise CredentialsError( - message='credentials not found', email=self._email) + raise CredentialsError(message='credentials not found', email=self._email) return creds diff --git a/pyproject.toml b/pyproject.toml index 7215a91..bdb9ed1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-oauth-token-manager" -version = "0.2.7" +version = "0.3.0" authors = [{ name = "David Harcombe", email = "david.harcombe@gmail.com" }] description = "API for managing stored OAuth credentials." readme = "README.md" From d4dd370bbca38b0e513b49487deb685364e701d9 Mon Sep 17 00:00:00 2001 From: David Harcombe Date: Thu, 19 Oct 2023 17:39:45 +0000 Subject: [PATCH 2/2] Bugfixes - Update `credentials.py` to unpack str copies of tokens - Update `secret_manager.py` to store tokens better --- auth/credentials.py | 3 ++- auth/datastore/secret_manager.py | 14 ++++++++------ pyproject.toml | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/auth/credentials.py b/auth/credentials.py index a4ae185..c457d78 100644 --- a/auth/credentials.py +++ b/auth/credentials.py @@ -15,6 +15,7 @@ from dataclasses import dataclass from datetime import datetime +import json from typing import Any, Dict, Mapping, Type, TypeVar, Union import pytz @@ -140,7 +141,7 @@ def credentials(self) -> oauth.Credentials: expiry = self._to_utc( datetime.now().astimezone(pytz.utc) + relativedelta(minutes=30)) if token := self.token_details: - creds = oauth.Credentials.from_authorized_user_info(token) + creds = oauth.Credentials.from_authorized_user_info(json.loads(token)) if creds.expired: creds.expiry = expiry diff --git a/auth/datastore/secret_manager.py b/auth/datastore/secret_manager.py index e184735..c92a78d 100644 --- a/auth/datastore/secret_manager.py +++ b/auth/datastore/secret_manager.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations +from io import BytesIO import json from typing import Any, List, Mapping, Optional, Type @@ -72,8 +73,10 @@ def store_document(self, id: str, document: Mapping[str, Any], document (Dict[str, Any]): The document to store. type (Optional[Type]): Unused. """ + b_document = BytesIO() + b_document.write(json.dumps(document).encode()) payload = secretmanager_v1.SecretPayload( - data=json.dumps(document).encode('utf-8')) + data=b_document.getvalue()) request = secretmanager_v1.AddSecretVersionRequest( parent=self.client.secret_path(self._project, id), payload=payload) @@ -101,14 +104,13 @@ def update_document(self, id: str, new_data: Mapping[str, Any], # Destroy other versions request = secretmanager_v1.ListSecretVersionsRequest( parent=self.client.secret_path(project=self._project, secret=id), - filter=f'state:enabled AND name!="{latest.name}"' + filter=f'state:enabled' # AND name!="{latest.name}"' ) version_list = self.client.list_secret_versions(request=request) for page in version_list.pages: for version in page.versions: - if version == new_version: - continue - else: + # Only delete older versions + if version.create_time < new_version.create_time: self.client.destroy_secret_version( secretmanager_v1.DestroySecretVersionRequest( name=version.name @@ -153,7 +155,7 @@ def get_document(self, id: str, type: Optional[Type] = None, try: request = secretmanager_v1.AccessSecretVersionRequest(name=secret) response = self.client.access_secret_version(request=request) - return json.loads(response.payload.data) + return json.loads(response.payload.data.decode('utf-8')) except Exception as e: print(e) return None diff --git a/pyproject.toml b/pyproject.toml index bdb9ed1..8bcc5f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-oauth-token-manager" -version = "0.3.0" +version = "0.4.0" authors = [{ name = "David Harcombe", email = "david.harcombe@gmail.com" }] description = "API for managing stored OAuth credentials." readme = "README.md"