From 16088cd0446735e31060556864def078ee189308 Mon Sep 17 00:00:00 2001 From: Derek Schlabach Date: Sat, 21 Sep 2024 13:35:55 -0500 Subject: [PATCH 1/4] Move tokenstorage v2 out of experimental --- ...0240920_144134_derek_move_tokenstorage.rst | 6 +++ .../experimental/globus_app/_types.py | 2 +- .../globus_app/_validating_token_storage.py | 2 +- src/globus_sdk/experimental/globus_app/app.py | 2 +- .../experimental/globus_app/config.py | 12 +++--- src/globus_sdk/experimental/tokenstorage.py | 43 +++++++++++++++++++ .../experimental/tokenstorage/__init__.py | 14 ------ src/globus_sdk/tokenstorage/__init__.py | 15 +++++++ src/globus_sdk/tokenstorage/v2/__init__.py | 14 ++++++ .../tokenstorage => tokenstorage/v2}/base.py | 4 +- .../tokenstorage => tokenstorage/v2}/json.py | 2 +- .../v2}/memory.py | 3 +- .../v2}/sqlite.py | 2 +- .../v2}/token_data.py | 0 .../v2}/conftest.py | 2 +- .../v2/test_common_tokenstorage.py} | 2 +- .../v2/test_json_tokenstorage.py} | 5 +-- .../v2/test_memory_tokenstorage.py} | 2 +- .../v2/test_sqlite_tokenstorage.py} | 5 +-- .../globus_app/test_authorizer_factory.py | 2 +- .../globus_app/test_globus_app.py | 12 +++--- .../test_validating_token_storage.py | 2 +- .../experimental/test_client_integration.py | 2 +- .../unit/experimental/test_legacy_support.py | 9 ++++ .../v2}/test_token_storage.py | 2 +- 25 files changed, 118 insertions(+), 48 deletions(-) create mode 100644 changelog.d/20240920_144134_derek_move_tokenstorage.rst create mode 100644 src/globus_sdk/experimental/tokenstorage.py delete mode 100644 src/globus_sdk/experimental/tokenstorage/__init__.py create mode 100644 src/globus_sdk/tokenstorage/v2/__init__.py rename src/globus_sdk/{experimental/tokenstorage => tokenstorage/v2}/base.py (99%) rename src/globus_sdk/{experimental/tokenstorage => tokenstorage/v2}/json.py (99%) rename src/globus_sdk/{experimental/tokenstorage => tokenstorage/v2}/memory.py (96%) rename src/globus_sdk/{experimental/tokenstorage => tokenstorage/v2}/sqlite.py (99%) rename src/globus_sdk/{experimental/tokenstorage => tokenstorage/v2}/token_data.py (100%) rename tests/functional/{tokenstorage_v2 => tokenstorage/v2}/conftest.py (98%) rename tests/functional/{tokenstorage_v2/test_common_tokenstorage_behaviors.py => tokenstorage/v2/test_common_tokenstorage.py} (98%) rename tests/functional/{tokenstorage_v2/test_json_token_storage.py => tokenstorage/v2/test_json_tokenstorage.py} (94%) rename tests/functional/{tokenstorage_v2/test_memory_token_storage.py => tokenstorage/v2/test_memory_tokenstorage.py} (96%) rename tests/functional/{tokenstorage_v2/test_sqlite_token_storage.py => tokenstorage/v2/test_sqlite_tokenstorage.py} (95%) rename tests/unit/{experimental => tokenstorage/v2}/test_token_storage.py (95%) diff --git a/changelog.d/20240920_144134_derek_move_tokenstorage.rst b/changelog.d/20240920_144134_derek_move_tokenstorage.rst new file mode 100644 index 00000000..1af905c7 --- /dev/null +++ b/changelog.d/20240920_144134_derek_move_tokenstorage.rst @@ -0,0 +1,6 @@ + +Changed +~~~~~~~ + +- TokenStorages have been moved from ``globus_sdk.experimental.tokenstorage`` + to ``globus_sdk.tokenstorage``. (:pr:`NUMBER`) diff --git a/src/globus_sdk/experimental/globus_app/_types.py b/src/globus_sdk/experimental/globus_app/_types.py index 5ffb3b65..1d0e0493 100644 --- a/src/globus_sdk/experimental/globus_app/_types.py +++ b/src/globus_sdk/experimental/globus_app/_types.py @@ -5,8 +5,8 @@ from globus_sdk import AuthLoginClient from globus_sdk._types import UUIDLike -from globus_sdk.experimental.tokenstorage import TokenStorage from globus_sdk.login_flows import LoginFlowManager +from globus_sdk.tokenstorage import TokenStorage if sys.version_info < (3, 8): from typing_extensions import Protocol, runtime_checkable diff --git a/src/globus_sdk/experimental/globus_app/_validating_token_storage.py b/src/globus_sdk/experimental/globus_app/_validating_token_storage.py index 803ffe3b..0f192904 100644 --- a/src/globus_sdk/experimental/globus_app/_validating_token_storage.py +++ b/src/globus_sdk/experimental/globus_app/_validating_token_storage.py @@ -3,8 +3,8 @@ import typing as t from globus_sdk import AuthClient, OAuthRefreshTokenResponse, OAuthTokenResponse, Scope -from globus_sdk.experimental.tokenstorage import TokenStorage, TokenStorageData from globus_sdk.scopes.consents import ConsentForest +from globus_sdk.tokenstorage import TokenStorage, TokenStorageData from .errors import ( IdentityMismatchError, diff --git a/src/globus_sdk/experimental/globus_app/app.py b/src/globus_sdk/experimental/globus_app/app.py index 758d5c3b..473bb55f 100644 --- a/src/globus_sdk/experimental/globus_app/app.py +++ b/src/globus_sdk/experimental/globus_app/app.py @@ -7,9 +7,9 @@ from globus_sdk import AuthClient, AuthLoginClient, GlobusSDKUsageError, Scope from globus_sdk._types import ScopeCollectionType, UUIDLike from globus_sdk.authorizers import GlobusAuthorizer -from globus_sdk.experimental.tokenstorage import TokenStorage from globus_sdk.gare import GlobusAuthorizationParameters from globus_sdk.scopes import AuthScopes, scopes_to_scope_list +from globus_sdk.tokenstorage import TokenStorage from ._types import TokenStorageProvider from ._validating_token_storage import ValidatingTokenStorage diff --git a/src/globus_sdk/experimental/globus_app/config.py b/src/globus_sdk/experimental/globus_app/config.py index 179a5036..eb05a642 100644 --- a/src/globus_sdk/experimental/globus_app/config.py +++ b/src/globus_sdk/experimental/globus_app/config.py @@ -4,17 +4,17 @@ import typing as t from globus_sdk.config import get_environment_name -from globus_sdk.experimental.tokenstorage import ( - JSONTokenStorage, - MemoryTokenStorage, - SQLiteTokenStorage, - TokenStorage, -) from globus_sdk.login_flows import ( CommandLineLoginFlowManager, LocalServerLoginFlowManager, LoginFlowManager, ) +from globus_sdk.tokenstorage import ( + JSONTokenStorage, + MemoryTokenStorage, + SQLiteTokenStorage, + TokenStorage, +) from ._types import ( LoginFlowManagerProvider, diff --git a/src/globus_sdk/experimental/tokenstorage.py b/src/globus_sdk/experimental/tokenstorage.py new file mode 100644 index 00000000..51b0a11c --- /dev/null +++ b/src/globus_sdk/experimental/tokenstorage.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +import sys +import typing as t + +__all__ = ( + "JSONTokenStorage", + "SQLiteTokenStorage", + "TokenStorage", + "FileTokenStorage", + "MemoryTokenStorage", + "TokenStorageData", +) + +# legacy aliases +# (when accessed, these will emit deprecation warnings) +if t.TYPE_CHECKING: + from globus_sdk.tokenstorage import ( + FileTokenStorage, + JSONTokenStorage, + MemoryTokenStorage, + SQLiteTokenStorage, + TokenStorage, + TokenStorageData, + ) +else: + + def __getattr__(name: str) -> t.Any: + import globus_sdk.tokenstorage as tokenstorage_module + from globus_sdk.exc import warn_deprecated + + warn_deprecated( + "'globus_sdk.experimental.tokenstorage' has been renamed to " + "'globus_sdk.tokenstorage'. " + f"Importing '{name}' from `globus_sdk.experimental` is deprecated. " + f"Use `globus_sdk.tokenstorage.{name}` instead." + ) + + value = getattr(tokenstorage_module, name, None) + if value is None: + raise AttributeError(f"module {__name__} has no attribute {name}") + setattr(sys.modules[__name__], name, value) + return value diff --git a/src/globus_sdk/experimental/tokenstorage/__init__.py b/src/globus_sdk/experimental/tokenstorage/__init__.py deleted file mode 100644 index 9765d90c..00000000 --- a/src/globus_sdk/experimental/tokenstorage/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -from globus_sdk.experimental.tokenstorage.base import FileTokenStorage, TokenStorage -from globus_sdk.experimental.tokenstorage.json import JSONTokenStorage -from globus_sdk.experimental.tokenstorage.memory import MemoryTokenStorage -from globus_sdk.experimental.tokenstorage.sqlite import SQLiteTokenStorage -from globus_sdk.experimental.tokenstorage.token_data import TokenStorageData - -__all__ = ( - "JSONTokenStorage", - "SQLiteTokenStorage", - "TokenStorage", - "FileTokenStorage", - "MemoryTokenStorage", - "TokenStorageData", -) diff --git a/src/globus_sdk/tokenstorage/__init__.py b/src/globus_sdk/tokenstorage/__init__.py index c1ce5954..35a508d7 100644 --- a/src/globus_sdk/tokenstorage/__init__.py +++ b/src/globus_sdk/tokenstorage/__init__.py @@ -5,6 +5,14 @@ SQLiteAdapter, StorageAdapter, ) +from .v2 import ( + FileTokenStorage, + JSONTokenStorage, + MemoryTokenStorage, + SQLiteTokenStorage, + TokenStorage, + TokenStorageData, +) __all__ = ( # v1 "StorageAdapter" Constructs @@ -13,4 +21,11 @@ "SimpleJSONFileAdapter", "SQLiteAdapter", "MemoryAdapter", + # v2 "TokenStorage" Constructs + "TokenStorage", + "TokenStorageData", + "FileTokenStorage", + "JSONTokenStorage", + "SQLiteTokenStorage", + "MemoryTokenStorage", ) diff --git a/src/globus_sdk/tokenstorage/v2/__init__.py b/src/globus_sdk/tokenstorage/v2/__init__.py new file mode 100644 index 00000000..4b6ca5bb --- /dev/null +++ b/src/globus_sdk/tokenstorage/v2/__init__.py @@ -0,0 +1,14 @@ +from .base import FileTokenStorage, TokenStorage +from .json import JSONTokenStorage +from .memory import MemoryTokenStorage +from .sqlite import SQLiteTokenStorage +from .token_data import TokenStorageData + +__all__ = ( + "TokenStorage", + "TokenStorageData", + "FileTokenStorage", + "JSONTokenStorage", + "SQLiteTokenStorage", + "MemoryTokenStorage", +) diff --git a/src/globus_sdk/experimental/tokenstorage/base.py b/src/globus_sdk/tokenstorage/v2/base.py similarity index 99% rename from src/globus_sdk/experimental/tokenstorage/base.py rename to src/globus_sdk/tokenstorage/v2/base.py index 7d132788..fc7a9277 100644 --- a/src/globus_sdk/experimental/tokenstorage/base.py +++ b/src/globus_sdk/tokenstorage/v2/base.py @@ -8,10 +8,10 @@ import sys import typing as t +from globus_sdk import GlobusSDKUsageError +from globus_sdk._types import UUIDLike from globus_sdk.services.auth import OAuthDependentTokenResponse, OAuthTokenResponse -from ... import GlobusSDKUsageError -from ..._types import UUIDLike from .token_data import TokenStorageData if t.TYPE_CHECKING: diff --git a/src/globus_sdk/experimental/tokenstorage/json.py b/src/globus_sdk/tokenstorage/v2/json.py similarity index 99% rename from src/globus_sdk/experimental/tokenstorage/json.py rename to src/globus_sdk/tokenstorage/v2/json.py index 98bb5230..6b9773f7 100644 --- a/src/globus_sdk/experimental/tokenstorage/json.py +++ b/src/globus_sdk/tokenstorage/v2/json.py @@ -3,9 +3,9 @@ import json import typing as t -from globus_sdk.experimental.tokenstorage.base import FileTokenStorage from globus_sdk.version import __version__ +from .base import FileTokenStorage from .token_data import TokenStorageData # use the non-annotation form of TypedDict to apply a non-identifier key diff --git a/src/globus_sdk/experimental/tokenstorage/memory.py b/src/globus_sdk/tokenstorage/v2/memory.py similarity index 96% rename from src/globus_sdk/experimental/tokenstorage/memory.py rename to src/globus_sdk/tokenstorage/v2/memory.py index 7dc31c7e..713d2bb0 100644 --- a/src/globus_sdk/experimental/tokenstorage/memory.py +++ b/src/globus_sdk/tokenstorage/v2/memory.py @@ -2,8 +2,7 @@ import typing as t -from globus_sdk.experimental.tokenstorage.base import TokenStorage - +from .base import TokenStorage from .token_data import TokenStorageData if t.TYPE_CHECKING: diff --git a/src/globus_sdk/experimental/tokenstorage/sqlite.py b/src/globus_sdk/tokenstorage/v2/sqlite.py similarity index 99% rename from src/globus_sdk/experimental/tokenstorage/sqlite.py rename to src/globus_sdk/tokenstorage/v2/sqlite.py index b47a1ccd..5d71145f 100644 --- a/src/globus_sdk/experimental/tokenstorage/sqlite.py +++ b/src/globus_sdk/tokenstorage/v2/sqlite.py @@ -7,9 +7,9 @@ import typing as t from globus_sdk import exc -from globus_sdk.experimental.tokenstorage.base import FileTokenStorage from globus_sdk.version import __version__ +from .base import FileTokenStorage from .token_data import TokenStorageData diff --git a/src/globus_sdk/experimental/tokenstorage/token_data.py b/src/globus_sdk/tokenstorage/v2/token_data.py similarity index 100% rename from src/globus_sdk/experimental/tokenstorage/token_data.py rename to src/globus_sdk/tokenstorage/v2/token_data.py diff --git a/tests/functional/tokenstorage_v2/conftest.py b/tests/functional/tokenstorage/v2/conftest.py similarity index 98% rename from tests/functional/tokenstorage_v2/conftest.py rename to tests/functional/tokenstorage/v2/conftest.py index 472ce2f9..2bbe171e 100644 --- a/tests/functional/tokenstorage_v2/conftest.py +++ b/tests/functional/tokenstorage/v2/conftest.py @@ -6,7 +6,7 @@ import globus_sdk from globus_sdk._testing import RegisteredResponse -from globus_sdk.experimental.tokenstorage import TokenStorageData +from globus_sdk.tokenstorage import TokenStorageData @pytest.fixture diff --git a/tests/functional/tokenstorage_v2/test_common_tokenstorage_behaviors.py b/tests/functional/tokenstorage/v2/test_common_tokenstorage.py similarity index 98% rename from tests/functional/tokenstorage_v2/test_common_tokenstorage_behaviors.py rename to tests/functional/tokenstorage/v2/test_common_tokenstorage.py index 48499995..1162b20f 100644 --- a/tests/functional/tokenstorage_v2/test_common_tokenstorage_behaviors.py +++ b/tests/functional/tokenstorage/v2/test_common_tokenstorage.py @@ -1,6 +1,6 @@ import pytest -from globus_sdk.experimental.tokenstorage import ( +from globus_sdk.tokenstorage import ( JSONTokenStorage, MemoryTokenStorage, SQLiteTokenStorage, diff --git a/tests/functional/tokenstorage_v2/test_json_token_storage.py b/tests/functional/tokenstorage/v2/test_json_tokenstorage.py similarity index 94% rename from tests/functional/tokenstorage_v2/test_json_token_storage.py rename to tests/functional/tokenstorage/v2/test_json_tokenstorage.py index 1a54dff0..606fab03 100644 --- a/tests/functional/tokenstorage_v2/test_json_token_storage.py +++ b/tests/functional/tokenstorage/v2/test_json_tokenstorage.py @@ -3,8 +3,7 @@ import pytest -from globus_sdk.experimental.tokenstorage import JSONTokenStorage -from globus_sdk.tokenstorage import SimpleJSONFileAdapter +from globus_sdk.tokenstorage import JSONTokenStorage, SimpleJSONFileAdapter from globus_sdk.version import __version__ IS_WINDOWS = os.name == "nt" @@ -90,7 +89,7 @@ def test_store_perms(json_file, mock_response): assert st_mode | 0o600 == 0o600 -def test_migrate_from_simple(json_file, mock_response): +def test_migrate_from_v1_adapter(json_file, mock_response): # write with a SimpleJSONFileAdapter old_adapter = SimpleJSONFileAdapter(json_file) old_adapter.store(mock_response) diff --git a/tests/functional/tokenstorage_v2/test_memory_token_storage.py b/tests/functional/tokenstorage/v2/test_memory_tokenstorage.py similarity index 96% rename from tests/functional/tokenstorage_v2/test_memory_token_storage.py rename to tests/functional/tokenstorage/v2/test_memory_tokenstorage.py index f149be17..9783e0b7 100644 --- a/tests/functional/tokenstorage_v2/test_memory_token_storage.py +++ b/tests/functional/tokenstorage/v2/test_memory_tokenstorage.py @@ -1,4 +1,4 @@ -from globus_sdk.experimental.tokenstorage import MemoryTokenStorage +from globus_sdk.tokenstorage import MemoryTokenStorage def test_store_and_get_token_data_by_resource_server( diff --git a/tests/functional/tokenstorage_v2/test_sqlite_token_storage.py b/tests/functional/tokenstorage/v2/test_sqlite_tokenstorage.py similarity index 95% rename from tests/functional/tokenstorage_v2/test_sqlite_token_storage.py rename to tests/functional/tokenstorage/v2/test_sqlite_tokenstorage.py index 924fc2a4..5662afc5 100644 --- a/tests/functional/tokenstorage_v2/test_sqlite_token_storage.py +++ b/tests/functional/tokenstorage/v2/test_sqlite_tokenstorage.py @@ -1,8 +1,7 @@ import pytest from globus_sdk import exc -from globus_sdk.experimental.tokenstorage import SQLiteTokenStorage -from globus_sdk.tokenstorage import SQLiteAdapter +from globus_sdk.tokenstorage import SQLiteAdapter, SQLiteTokenStorage @pytest.fixture @@ -116,7 +115,7 @@ def test_iter_namespaces(mock_response, db_file, make_adapter): assert set(adapter.iter_namespaces()) == {"foo", "bar"} -def test_backwards_compatible_storage(mock_response, db_file, make_adapter): +def test_migrate_from_v1_adapter(mock_response, db_file, make_adapter): # store data with SQLiteAdapter old_adapter = SQLiteAdapter(db_file) old_adapter.store(mock_response) diff --git a/tests/unit/experimental/globus_app/test_authorizer_factory.py b/tests/unit/experimental/globus_app/test_authorizer_factory.py index acefdbb0..3d67febf 100644 --- a/tests/unit/experimental/globus_app/test_authorizer_factory.py +++ b/tests/unit/experimental/globus_app/test_authorizer_factory.py @@ -12,7 +12,7 @@ ExpiredTokenError, MissingTokenError, ) -from globus_sdk.experimental.tokenstorage import TokenStorageData +from globus_sdk.tokenstorage import TokenStorageData def make_mock_token_response(token_number=1): diff --git a/tests/unit/experimental/globus_app/test_globus_app.py b/tests/unit/experimental/globus_app/test_globus_app.py index e4321fa4..f5998ad1 100644 --- a/tests/unit/experimental/globus_app/test_globus_app.py +++ b/tests/unit/experimental/globus_app/test_globus_app.py @@ -24,12 +24,6 @@ RefreshTokenAuthorizerFactory, UserApp, ) -from globus_sdk.experimental.tokenstorage import ( - JSONTokenStorage, - MemoryTokenStorage, - SQLiteTokenStorage, - TokenStorageData, -) from globus_sdk.gare import GlobusAuthorizationParameters from globus_sdk.login_flows import ( CommandLineLoginFlowManager, @@ -38,6 +32,12 @@ ) from globus_sdk.scopes import AuthScopes, Scope from globus_sdk.services.auth import OAuthTokenResponse +from globus_sdk.tokenstorage import ( + JSONTokenStorage, + MemoryTokenStorage, + SQLiteTokenStorage, + TokenStorageData, +) def _mock_token_data_by_rs(): diff --git a/tests/unit/experimental/globus_app/test_validating_token_storage.py b/tests/unit/experimental/globus_app/test_validating_token_storage.py index 461b3875..1a2d2497 100644 --- a/tests/unit/experimental/globus_app/test_validating_token_storage.py +++ b/tests/unit/experimental/globus_app/test_validating_token_storage.py @@ -22,8 +22,8 @@ MissingTokenError, UnmetScopeRequirementsError, ) -from globus_sdk.experimental.tokenstorage import MemoryTokenStorage from globus_sdk.scopes.consents import ConsentForest +from globus_sdk.tokenstorage import MemoryTokenStorage from tests.common import make_consent_forest diff --git a/tests/unit/experimental/test_client_integration.py b/tests/unit/experimental/test_client_integration.py index 96507215..3c7b1afa 100644 --- a/tests/unit/experimental/test_client_integration.py +++ b/tests/unit/experimental/test_client_integration.py @@ -6,7 +6,7 @@ from globus_sdk import GlobusSDKUsageError from globus_sdk._testing import load_response from globus_sdk.experimental.globus_app import GlobusApp, GlobusAppConfig, UserApp -from globus_sdk.experimental.tokenstorage import MemoryTokenStorage +from globus_sdk.tokenstorage import MemoryTokenStorage @pytest.fixture diff --git a/tests/unit/experimental/test_legacy_support.py b/tests/unit/experimental/test_legacy_support.py index 0d8b87ff..e1b07c87 100644 --- a/tests/unit/experimental/test_legacy_support.py +++ b/tests/unit/experimental/test_legacy_support.py @@ -27,3 +27,12 @@ def test_login_flow_manager_importable_from_experimental(): LocalServerLoginFlowManager, LoginFlowManager, ) + + +def test_tokenstorage_importable_from_experimental(): + with pytest.warns(DeprecationWarning): + from globus_sdk.experimental.tokenstorage import ( # noqa: F401 + JSONTokenStorage, + MemoryTokenStorage, + SQLiteTokenStorage, + ) diff --git a/tests/unit/experimental/test_token_storage.py b/tests/unit/tokenstorage/v2/test_token_storage.py similarity index 95% rename from tests/unit/experimental/test_token_storage.py rename to tests/unit/tokenstorage/v2/test_token_storage.py index 833c61e2..5f991943 100644 --- a/tests/unit/experimental/test_token_storage.py +++ b/tests/unit/tokenstorage/v2/test_token_storage.py @@ -1,7 +1,7 @@ import pytest from globus_sdk import GlobusSDKUsageError -from globus_sdk.experimental.tokenstorage.base import _slugify_app_name +from globus_sdk.tokenstorage.v2.base import _slugify_app_name @pytest.mark.parametrize( From 7afa1fe23704c8bc6d05de5f66cea42c7346b0e7 Mon Sep 17 00:00:00 2001 From: Derek Schlabach Date: Thu, 26 Sep 2024 13:24:57 -0500 Subject: [PATCH 2/4] Move TokenStorage doc -> Storage Adapters (Legacy) --- docs/authorization/index.rst | 1 + docs/authorization/token_caching/index.rst | 20 +++ .../token_caching/storage_adapters.rst | 133 ++++++++++++++++++ .../token_caching/token_storages.rst | 6 + docs/index.rst | 1 - docs/tokenstorage.rst | 127 +---------------- docs/tutorial.rst | 2 +- 7 files changed, 163 insertions(+), 127 deletions(-) create mode 100644 docs/authorization/token_caching/index.rst create mode 100644 docs/authorization/token_caching/storage_adapters.rst create mode 100644 docs/authorization/token_caching/token_storages.rst diff --git a/docs/authorization/index.rst b/docs/authorization/index.rst index 62084a69..378ff2dd 100644 --- a/docs/authorization/index.rst +++ b/docs/authorization/index.rst @@ -9,4 +9,5 @@ Components of the Globus SDK which handle application authorization. globus_authorizers scopes_and_consents/index login_flows + token_caching/index gare diff --git a/docs/authorization/token_caching/index.rst b/docs/authorization/token_caching/index.rst new file mode 100644 index 00000000..9276130e --- /dev/null +++ b/docs/authorization/token_caching/index.rst @@ -0,0 +1,20 @@ + +Token Caching +============= + +The documentation in this section provides references for interfaces and standard +implementations for caching OAuth2 tokens. While there are two distinct class +hierarchies are, :ref:`token_storages` and its predecessor :ref:`storage_adapters`, we +recommend using the former. ``TokenStorage`` is a newer iteration of the token storage +interface and includes a superset of the functionality previously supported in +``StorageAdapter``. + +All constructs from both hierarchies are importable from the ``globus_sdk.tokenstorage`` +namespace. + +.. toctree:: + :maxdepth: 1 + + token_storages + storage_adapters + diff --git a/docs/authorization/token_caching/storage_adapters.rst b/docs/authorization/token_caching/storage_adapters.rst new file mode 100644 index 00000000..20684e93 --- /dev/null +++ b/docs/authorization/token_caching/storage_adapters.rst @@ -0,0 +1,133 @@ +.. _storage_adapters: + +Storage Adapters (Legacy) +========================= + +.. warning:: + + The class hierarchy documented here is legacy. + + We recommend using the newer class hierarchy documented at :ref:`token_storages`. + +The StorageAdapter component provides a way of storing and loading the tokens +received from authentication and token refreshes. + +Usage +----- + +StorageAdapter is available under the name ``globus_sdk.tokenstorage``. + +Storage adapters are the main objects of this subpackage. Primarily, usage +should revolve around creating a storage adapter, potentially loading data from +it, and using it as the ``on_refresh`` handler for an authorizer. + +For example: + +.. code-block:: python + + import os + import globus_sdk + from globus_sdk.tokenstorage import SimpleJSONFileAdapter + + my_file_adapter = SimpleJSONFileAdapter(os.path.expanduser("~/mytokens.json")) + + if not my_file_adapter.file_exists(): + # ... do a login flow, getting back initial tokens + # elided for simplicity here + token_response = ... + # now store the tokens, and pull out the tokens for the + # resource server we want + my_file_adapter.store(token_response) + by_rs = token_response.by_resource_server + tokens = by_rs["transfer.api.globus.org"] + else: + # otherwise, we already did this whole song-and-dance, so just + # load the tokens from that file + tokens = my_file_adapter.get_token_data("transfer.api.globus.org") + + + # RereshTokenAuthorizer and ClientCredentialsAuthorizer both use + # `on_refresh` callbacks + # this feature is therefore only relevant for those auth types + # + # auth_client is the internal auth client used for refreshes, + # and which was used in the login flow + # note that this is all normal authorizer usage wherein + # my_file_adapter is providing the on_refresh callback + auth_client = ... + authorizer = globus_sdk.RefreshTokenAuthorizer( + tokens["refresh_token"], + auth_client, + access_token=tokens["access_token"], + expires_at=tokens["expires_at_seconds"], + on_refresh=my_file_adapter.on_refresh, + ) + + # or, for client credentials + authorizer = globus_sdk.ClientCredentialsAuthorizer( + auth_client, + ["urn:globus:auth:transfer.api.globus.org:all"], + access_token=tokens["access_token"], + expires_at=tokens["expires_at_seconds"], + on_refresh=my_file_adapter.on_refresh, + ) + + # and then use the authorizer on a client! + tc = globus_sdk.TransferClient(authorizer=authorizer) + + +Complete Example Usage +~~~~~~~~~~~~~~~~~~~~~~ + +The :ref:`Group Listing With Token Storage Script ` +provides complete and runnable example which leverages ``tokenstorage``. + + +Adapter Types +------------- + +.. module:: globus_sdk.tokenstorage + +``globus_sdk.tokenstorage`` provides base classes for building your own storage +adapters, and several complete adapters. + +The :class:`SimpleJSONFileAdapter` is good for the "simplest possible" +persistent storage, using a JSON file to store token data. + +:class:`MemoryAdapter` is even simpler still, and is great for writing and +testing code which uses the `StorageAdapter` interface backed by an in-memory +structure. + +The :class:`SQLiteAdapter` is more complex, for applications like the +globus-cli which need to store various tokens and additional configuration. In +addition to basic token storage, the :class:`SQLiteAdapter` provides for namespacing +of the token data, and for additional configuration storage. + +Reference +--------- + +.. autoclass:: StorageAdapter + :members: + :member-order: bysource + :show-inheritance: + +.. autoclass:: MemoryAdapter + :members: + :member-order: bysource + :show-inheritance: + +.. autoclass:: FileAdapter + :members: + :member-order: bysource + :show-inheritance: + +.. autoclass:: SimpleJSONFileAdapter + :members: + :member-order: bysource + :show-inheritance: + +.. autoclass:: SQLiteAdapter + :members: + :member-order: bysource + :show-inheritance: + diff --git a/docs/authorization/token_caching/token_storages.rst b/docs/authorization/token_caching/token_storages.rst new file mode 100644 index 00000000..24c04db4 --- /dev/null +++ b/docs/authorization/token_caching/token_storages.rst @@ -0,0 +1,6 @@ +.. _token_storages: + +Token Storages +============== + +TODO! diff --git a/docs/index.rst b/docs/index.rst index b739ebdc..131454ed 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -34,7 +34,6 @@ Table of Contents services/index local_endpoints Authorization - tokenstorage config core/index diff --git a/docs/tokenstorage.rst b/docs/tokenstorage.rst index 194413b1..93970667 100644 --- a/docs/tokenstorage.rst +++ b/docs/tokenstorage.rst @@ -1,126 +1,3 @@ -.. _tokenstorage: +:orphan: -TokenStorage -============ - -The TokenStorage component provides a way of storing and loading the tokens -received from authentication and token refreshes. - -Usage ------ - -TokenStorage is available under the name ``globus_sdk.tokenstorage``. - -Storage adapters are the main objects of this subpackage. Primarily, usage -should revolve around creating a storage adapter, potentially loading data from -it, and using it as the ``on_refresh`` handler for an authorizer. - -For example: - -.. code-block:: python - - import os - import globus_sdk - from globus_sdk.tokenstorage import SimpleJSONFileAdapter - - my_file_adapter = SimpleJSONFileAdapter(os.path.expanduser("~/mytokens.json")) - - if not my_file_adapter.file_exists(): - # ... do a login flow, getting back initial tokens - # elided for simplicity here - token_response = ... - # now store the tokens, and pull out the tokens for the - # resource server we want - my_file_adapter.store(token_response) - by_rs = token_response.by_resource_server - tokens = by_rs["transfer.api.globus.org"] - else: - # otherwise, we already did this whole song-and-dance, so just - # load the tokens from that file - tokens = my_file_adapter.get_token_data("transfer.api.globus.org") - - - # RereshTokenAuthorizer and ClientCredentialsAuthorizer both use - # `on_refresh` callbacks - # this feature is therefore only relevant for those auth types - # - # auth_client is the internal auth client used for refreshes, - # and which was used in the login flow - # note that this is all normal authorizer usage wherein - # my_file_adapter is providing the on_refresh callback - auth_client = ... - authorizer = globus_sdk.RefreshTokenAuthorizer( - tokens["refresh_token"], - auth_client, - access_token=tokens["access_token"], - expires_at=tokens["expires_at_seconds"], - on_refresh=my_file_adapter.on_refresh, - ) - - # or, for client credentials - authorizer = globus_sdk.ClientCredentialsAuthorizer( - auth_client, - ["urn:globus:auth:transfer.api.globus.org:all"], - access_token=tokens["access_token"], - expires_at=tokens["expires_at_seconds"], - on_refresh=my_file_adapter.on_refresh, - ) - - # and then use the authorizer on a client! - tc = globus_sdk.TransferClient(authorizer=authorizer) - - -Complete Example Usage -~~~~~~~~~~~~~~~~~~~~~~ - -The :ref:`Group Listing With Token Storage Script ` -provides complete and runnable example which leverages ``tokenstorage``. - - -Adapter Types -------------- - -.. module:: globus_sdk.tokenstorage - -``globus_sdk.tokenstorage`` provides base classes for building your own storage -adapters, and several complete adapters. - -The :class:`SimpleJSONFileAdapter` is good for the "simplest possible" -persistent storage, using a JSON file to store token data. - -:class:`MemoryAdapter` is even simpler still, and is great for writing and -testing code which uses the `StorageAdapter` interface backed by an in-memory -structure. - -The :class:`SQLiteAdapter` is more complex, for applications like the -globus-cli which need to store various tokens and additional configuration. In -addition to basic token storage, the :class:`SQLiteAdapter` provides for namespacing -of the token data, and for additional configuration storage. - -Reference ---------- - -.. autoclass:: StorageAdapter - :members: - :member-order: bysource - :show-inheritance: - -.. autoclass:: MemoryAdapter - :members: - :member-order: bysource - :show-inheritance: - -.. autoclass:: FileAdapter - :members: - :member-order: bysource - :show-inheritance: - -.. autoclass:: SimpleJSONFileAdapter - :members: - :member-order: bysource - :show-inheritance: - -.. autoclass:: SQLiteAdapter - :members: - :member-order: bysource - :show-inheritance: +The documentation which was found on this page has moved to :ref:`storage_adapters`. diff --git a/docs/tutorial.rst b/docs/tutorial.rst index f27e4d01..d5b33dbe 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -123,7 +123,7 @@ are useful for further reading: * :ref:`using GlobusAuthorizer objects ` handle passing tokens to Globus, and may handle token expiration -* :ref:`TokenStorage ` objects handle storage of tokens +* :ref:`storage_adapters` objects handle storage of tokens These are covered by several of the available :ref:`Examples ` as well. From fc320fda5f616512eb39e362cb36d5ccc7319f35 Mon Sep 17 00:00:00 2001 From: Derek Schlabach Date: Fri, 27 Sep 2024 10:57:42 -0500 Subject: [PATCH 3/4] Document tokenstorage interface --- .../token_caching/token_storages.rst | 42 ++++++++++++- src/globus_sdk/tokenstorage/v2/base.py | 61 +++++++++---------- src/globus_sdk/tokenstorage/v2/json.py | 29 +++++---- src/globus_sdk/tokenstorage/v2/memory.py | 7 ++- src/globus_sdk/tokenstorage/v2/sqlite.py | 43 ++++++------- src/globus_sdk/tokenstorage/v2/token_data.py | 44 ++++++------- 6 files changed, 130 insertions(+), 96 deletions(-) diff --git a/docs/authorization/token_caching/token_storages.rst b/docs/authorization/token_caching/token_storages.rst index 24c04db4..c4b6a9e5 100644 --- a/docs/authorization/token_caching/token_storages.rst +++ b/docs/authorization/token_caching/token_storages.rst @@ -1,6 +1,46 @@ .. _token_storages: +.. currentmodule:: globus_sdk.tokenstorage + Token Storages ============== -TODO! +Interacting with globus services requires the use of globus auth issued OAuth2 tokens. +To assist in reuse of these tokens, the SDK provides an interface to store and +retrieve this data across different storage backends. + +In addition to the interface, :class:`TokenStorage`, the SDK provides concrete +implementations for some of the most common storage backends: + +- :class:`JSONTokenStorage` for storing tokens in a local JSON file. +- :class:`SQLiteTokenStorage` for storing tokens in a local SQLite database. +- :class:`MemoryTokenStorage` for storing tokens in process memory. + + +Reference +--------- + +.. autoclass:: TokenStorage + :members: + :member-order: bysource + + +.. autoclass:: TokenStorageData + :members: + :member-order: bysource + + +File-based Token Storages +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: JSONTokenStorage + +.. autoclass:: SQLiteTokenStorage + :members: close, iter_namespaces + + +Ephemeral Token Storages +^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autoclass:: MemoryTokenStorage + diff --git a/src/globus_sdk/tokenstorage/v2/base.py b/src/globus_sdk/tokenstorage/v2/base.py index fc7a9277..54f783fa 100644 --- a/src/globus_sdk/tokenstorage/v2/base.py +++ b/src/globus_sdk/tokenstorage/v2/base.py @@ -20,19 +20,15 @@ class TokenStorage(metaclass=abc.ABCMeta): """ - Abstract base class for interacting with an underlying storage system to manage - storage of token data. + The interface for interacting with a store of :class:`TokenStorageData` objects. - The ``namespace`` is a user-supplied way of partitioning data, and any token - response data passed to the storage adapter are stored indexed by - *resource_server*. If you have a more complex use-case in which this scheme will be - insufficient, you should encode that in your choice of ``namespace`` values. + Implementations must partition their token data objects by `namespace`. + Within a namespace, token data must be indexed by `resource_server`. + + :param namespace: A unique string for partitioning token data (Default: "DEFAULT"). """ def __init__(self, namespace: str = "DEFAULT") -> None: - """ - :param namespace: A user-supplied namespace for partitioning token data. - """ self.namespace = namespace @abc.abstractmethod @@ -40,51 +36,43 @@ def store_token_data_by_resource_server( self, token_data_by_resource_server: t.Mapping[str, TokenStorageData] ) -> None: """ - Store token data in underlying storage partitioned by the resource server - and the current namespace. + Store token data for one or more resource server in the current namespace. - :param token_data_by_resource_server: a ``dict`` of ``TokenStorageData`` objects - indexed by their ``resource_server``. + :param token_data_by_resource_server: mapping of resource server to token data. """ @abc.abstractmethod def get_token_data_by_resource_server(self) -> dict[str, TokenStorageData]: """ - Lookup all token data under the current namespace in the underlying storage. + Retrieve all token data stored in the current namespace. - Returns a dict of ``TokenStorageData`` objects indexed by their resource server. + :returns: a dict of ``TokenStorageData`` objects indexed by their + resource server. """ def get_token_data(self, resource_server: str) -> TokenStorageData | None: """ - Lookup token data for a resource server in the underlying storage - under the current namespace. - - Either returns a ``TokenStorageData`` object containing tokens and metadata for - the given resource server or ``None`` indicating that there was no data for - that resource server. + Retrieve token data for a particular resource server in the current namespace. - :param resource_server: The resource_server string to get token data for + :param resource_server: The resource_server string to get token data for. + :returns: token data if found or else None. """ return self.get_token_data_by_resource_server().get(resource_server) @abc.abstractmethod def remove_token_data(self, resource_server: str) -> bool: """ - Remove all token data for a resource server from the underlying storage under - the current namespace. + Remove token data for a resource server in the current namespace. - Returns True if token data was deleted, False if none was found to delete. - - :param resource_server: The resource server string to remove token data for + :param resource_server: The resource server string to remove token data for. + :returns: True if token data was deleted, False if none was found to delete. """ def store_token_response(self, token_response: OAuthTokenResponse) -> None: """ - Wrapper around ``store_token_data_by_resource_server`` that accepts an - ``OAuthTokenResponse``. + Store token data from an :class:`OAuthTokenResponse` in the current namespace. - :param token_response: An ``OAuthTokenResponse`` from an authentication flow + :param token_response: A token response object from an authentication flow. """ token_data_by_resource_server = {} @@ -129,9 +117,16 @@ def _extract_identity_id(self, token_response: OAuthTokenResponse) -> str | None class FileTokenStorage(TokenStorage, metaclass=abc.ABCMeta): """ - File adapters are for single-user cases, where we can assume that there's a - simple file-per-user and users are only ever attempting to read their own - files. + A base class for token storages which store tokens on a local file. + + Common functionality for file-based token storages like file creation and class + instantiation for a GlobusApp is defined here. + + :cvar file_format: The file format suffix associated with files of this type. This + is used when constructing the file path for a GlobusApp. + + :param filepath: The path to a file where token data should be stored. + :param namespace: A unique string for partitioning token data (Default: "DEFAULT"). """ # File suffix associated with files of this type (e.g., "csv") diff --git a/src/globus_sdk/tokenstorage/v2/json.py b/src/globus_sdk/tokenstorage/v2/json.py index 6b9773f7..9ecb9f88 100644 --- a/src/globus_sdk/tokenstorage/v2/json.py +++ b/src/globus_sdk/tokenstorage/v2/json.py @@ -21,18 +21,23 @@ class _JSONFileData(_JSONFileData_0): # pylint: disable=inherit-non-class class JSONTokenStorage(FileTokenStorage): """ - A storage adapter for storing token data in JSON files. + A token storage which stores token data on disk in json files. + + This class defines a `format_version` which determines what the specific data shape. + Any data in a `supported_version` format which is not the primary `format_version` + will be automatically rewritten. + + See :class:`TokenStorage` for common interface details. + + :cvar "2.0" format_version: The data format version used when writing data. + :cvar ("1.0", "2.0") supported_versions: The list of data format versions which can + be read. + :param filepath: The path to a json file where token data should be stored. + :param namespace: A unique string for partitioning token data (Default: "DEFAULT"). """ - # the version for the current data format used by the file adapter. - # 1.0 was used by ``SimpleJSONFileAdapter``. If ``JSONFileAdapter`` is - # pointed at storage used by a ``SimpleJSONFileAdapter` it will be converted to 2.0 - # and no longer usable by ``SimpleJSONFileAdapter``. format_version = "2.0" - - # the supported versions (data not in these versions causes an error) supported_versions = ("1.0", "2.0") - file_format = "json" def _invalid(self, msg: str) -> t.NoReturn: @@ -119,16 +124,16 @@ def store_token_data_by_resource_server( self, token_data_by_resource_server: t.Mapping[str, TokenStorageData] ) -> None: """ - Store token data as JSON data in ``self.filepath`` under the current namespace + Store token data for one or more resource server in the current namespace - Additionally will write the version of ``globus_sdk``which was in use. + Token data, alongside globus-sdk version info, is serialized to json before + being written to the file at ``self.filepath``. Under the assumption that this may be running on a system with multiple local users, this sets the umask such that only the owner of the resulting file can read or write it. - :param token_data_by_resource_server: a ``dict`` of ``TokenStorageData`` objects - indexed by their ``resource_server``. + :param token_data_by_resource_server: mapping of resource server to token data. """ to_write = self._load() diff --git a/src/globus_sdk/tokenstorage/v2/memory.py b/src/globus_sdk/tokenstorage/v2/memory.py index 713d2bb0..0db9c8c1 100644 --- a/src/globus_sdk/tokenstorage/v2/memory.py +++ b/src/globus_sdk/tokenstorage/v2/memory.py @@ -12,9 +12,12 @@ class MemoryTokenStorage(TokenStorage): """ - A token storage adapter which stores token data in process memory. + A token storage which holds tokens in-memory. + All token data is lost when the process exits. - Tokens are lost when the process exits. + See :class:`TokenStorage` for common interface details. + + :param namespace: A unique string for partitioning token data (Default: "DEFAULT"). """ def __init__(self, *, namespace: str = "DEFAULT") -> None: diff --git a/src/globus_sdk/tokenstorage/v2/sqlite.py b/src/globus_sdk/tokenstorage/v2/sqlite.py index 5d71145f..bacd5662 100644 --- a/src/globus_sdk/tokenstorage/v2/sqlite.py +++ b/src/globus_sdk/tokenstorage/v2/sqlite.py @@ -15,16 +15,17 @@ class SQLiteTokenStorage(FileTokenStorage): """ - A storage adapter for storing token data in sqlite databases. + A token storage which stores token data on disk in sqlite databases. - SQLite adapters are for more complex cases, where there may be multiple users or - "profiles" in play, and additionally a dynamic set of resource servers which need to - be stored in an extensible way. + See :class:`TokenStorage` for common interface details. - The ``connect_params`` is an optional dictionary whose elements are passed directly - to the underlying ``sqlite3.connect()`` method, enabling developers to fine-tune the - connection to the SQLite database. Refer to the ``sqlite3.connect()`` - documentation for SQLite-specific parameters. + :param filepath: The path on disk to a SQLite database file. + :param connect_params: A dictionary of parameters to pass to ``sqlite3.connect()``. + :param namespace: A unique string for partitioning token data (Default: "DEFAULT"). + + :raises GlobusSDKUsageError: If the filepath is ":memory:". This usage-mode is not + supported in this class; use :class:`MemoryTokenStorage` instead if in-memory + token storage is desired. """ file_format = "db" @@ -36,12 +37,6 @@ def __init__( connect_params: dict[str, t.Any] | None = None, namespace: str = "DEFAULT", ) -> None: - """ - :param filepath: The path of the DB file to write to and read from. - :param connect_params: A pass-through dictionary for fine-tuning the SQLite - connection. - :param namespace: A user-supplied namespace for partitioning token data. - """ if filepath == ":memory:": raise exc.GlobusSDKUsageError( "SQLiteTokenStorage cannot be used with a ':memory:' database. " @@ -110,11 +105,11 @@ def store_token_data_by_resource_server( self, token_data_by_resource_server: t.Mapping[str, TokenStorageData] ) -> None: """ - Given a dict of token data indexed by resource server, convert the data into - JSON dicts and write it to ``self.filepath`` under the current namespace + Store token data for one or more resource server in the current namespace. + + Token data is json-serialized before being inserted into the database. - :param token_data_by_resource_server: a ``dict`` of ``TokenStorageData`` objects - indexed by their ``resource_server``. + :param token_data_by_resource_server: mapping of resource server to token data. """ pairs = [] for resource_server, token_data in token_data_by_resource_server.items(): @@ -134,7 +129,8 @@ def get_token_data_by_resource_server(self) -> dict[str, TokenStorageData]: """ Lookup all token data under the current namespace from the database. - Returns a dict of ``TokenStorageData`` objects indexed by their resource server. + :returns: a dict of ``TokenStorageData`` objects indexed by their + resource server. """ ret: dict[str, TokenStorageData] = {} for row in self._connection.execute( @@ -154,9 +150,8 @@ def remove_token_data(self, resource_server: str) -> bool: You can use this as part of a logout command implementation, loading token data as a dict, and then deleting the data for each resource server. - Returns True if token data was deleted, False if none was found to delete. - :param resource_server: The name of the resource server to remove from the DB + :returns: True if token data was deleted, False if none was found to delete. """ rowcount = self._connection.execute( "DELETE FROM token_storage WHERE namespace=? AND resource_server=?", @@ -166,11 +161,7 @@ def remove_token_data(self, resource_server: str) -> bool: return rowcount != 0 def iter_namespaces(self) -> t.Iterator[str]: - """ - Iterate over the namespaces which are in use in this storage adapter's database. - The presence of tokens for a namespace does not indicate that those tokens are - valid, only that they have been stored and have not been removed. - """ + """Iterate over all distinct namespaces in the sqlite database.""" seen: set[str] = set() for row in self._connection.execute( "SELECT DISTINCT namespace FROM token_storage;" diff --git a/src/globus_sdk/tokenstorage/v2/token_data.py b/src/globus_sdk/tokenstorage/v2/token_data.py index 8b0b59df..76fa97ec 100644 --- a/src/globus_sdk/tokenstorage/v2/token_data.py +++ b/src/globus_sdk/tokenstorage/v2/token_data.py @@ -8,36 +8,36 @@ class TokenStorageData(Serializable): """ - Data class containing tokens and metadata for a specific resource server used - as the python interface for ``TokenStorage``. - Contains the following attributes: + Data class for tokens and token metadata issued by a globus auth token grant. + For storage and retrieval of these objects, see :class:`TokenStorage`. - resource_server: str - The name of the resource server this token data is valid for + Tokens are scoped to a specific user/client (`identity_id`) performing + specific operations (`scope`) with a specific service (`resource_server`). - identity_id: str - A UUID string for the user this token data was granted to. This value may - be None if the original token grant did not include the "openid" scope + :ivar str resource_server: The resource server this for which this token data was + granted. + :ivar str identity_id: The primary identity id of the user or client which + requested this token. This will be None if an identity id was not extractable + from the token grant response. - scope: str - A space separated list of scopes these tokens provide access to. + :ivar str scope: A space separated list of scopes that this token data provides + access to. - access_token: str - An access token that can be used for authentication with Globus APIs. + :ivar str access_token: A globus auth issued OAuth2 access token. Used for + authentication when interacting with service APIs. - refresh_token: str | None - A refresh token that can be used for refresh token grants. This value may be - None if the original token grant did not allow for refresh tokens. + :ivar str | None refresh_token: A globus auth issued OAuth2 refresh token. Used to + obtain new access tokens when the current one expires. This value will be None + if the original token grant did not request refresh tokens. - expires_at_seconds: int - A POSIX timestamp for the time when access_token expires. + :ivar int expires_at_seconds: An epoch seconds timestamp for when the associated + access_token expires. - token_type: str | None - The token type of access_token, currently this will always be "Bearer" if present + :ivar str | None token_type: The token type of access_token, currently this will + always be "Bearer" if present - extra: dict | None - A dictionary of additional fields that were provided. May be used for - forward/backward compatibility. + :param extra: An optional dictionary of additional fields to include. Included for + forward/backward compatibility. """ def __init__( From b260261771346317b253566913eceb71a7c9580e Mon Sep 17 00:00:00 2001 From: Derek Schlabach Date: Fri, 27 Sep 2024 12:04:59 -0500 Subject: [PATCH 4/4] Docstring pedantry --- docs/authorization/token_caching/index.rst | 2 +- .../token_caching/storage_adapters.rst | 2 +- docs/authorization/token_caching/token_storages.rst | 2 +- src/globus_sdk/tokenstorage/v2/base.py | 6 +++--- src/globus_sdk/tokenstorage/v2/json.py | 11 ++++++----- src/globus_sdk/tokenstorage/v2/sqlite.py | 13 +++++++------ src/globus_sdk/tokenstorage/v2/token_data.py | 9 +++++---- 7 files changed, 24 insertions(+), 21 deletions(-) diff --git a/docs/authorization/token_caching/index.rst b/docs/authorization/token_caching/index.rst index 9276130e..fe92037e 100644 --- a/docs/authorization/token_caching/index.rst +++ b/docs/authorization/token_caching/index.rst @@ -4,7 +4,7 @@ Token Caching The documentation in this section provides references for interfaces and standard implementations for caching OAuth2 tokens. While there are two distinct class -hierarchies are, :ref:`token_storages` and its predecessor :ref:`storage_adapters`, we +hierarchies, :ref:`token_storages` and its predecessor :ref:`storage_adapters`, we recommend using the former. ``TokenStorage`` is a newer iteration of the token storage interface and includes a superset of the functionality previously supported in ``StorageAdapter``. diff --git a/docs/authorization/token_caching/storage_adapters.rst b/docs/authorization/token_caching/storage_adapters.rst index 20684e93..b7148753 100644 --- a/docs/authorization/token_caching/storage_adapters.rst +++ b/docs/authorization/token_caching/storage_adapters.rst @@ -80,7 +80,7 @@ Complete Example Usage ~~~~~~~~~~~~~~~~~~~~~~ The :ref:`Group Listing With Token Storage Script ` -provides complete and runnable example which leverages ``tokenstorage``. +provides a complete and runnable example which leverages ``tokenstorage``. Adapter Types diff --git a/docs/authorization/token_caching/token_storages.rst b/docs/authorization/token_caching/token_storages.rst index c4b6a9e5..f0a90f2e 100644 --- a/docs/authorization/token_caching/token_storages.rst +++ b/docs/authorization/token_caching/token_storages.rst @@ -5,7 +5,7 @@ Token Storages ============== -Interacting with globus services requires the use of globus auth issued OAuth2 tokens. +Interacting with Globus services requires the use of Globus Auth-issued OAuth2 tokens. To assist in reuse of these tokens, the SDK provides an interface to store and retrieve this data across different storage backends. diff --git a/src/globus_sdk/tokenstorage/v2/base.py b/src/globus_sdk/tokenstorage/v2/base.py index 54f783fa..c9b7359b 100644 --- a/src/globus_sdk/tokenstorage/v2/base.py +++ b/src/globus_sdk/tokenstorage/v2/base.py @@ -22,8 +22,8 @@ class TokenStorage(metaclass=abc.ABCMeta): """ The interface for interacting with a store of :class:`TokenStorageData` objects. - Implementations must partition their token data objects by `namespace`. - Within a namespace, token data must be indexed by `resource_server`. + Implementations must partition their token data objects by ``namespace``. + Within a namespace, token data must be indexed by ``resource_server``. :param namespace: A unique string for partitioning token data (Default: "DEFAULT"). """ @@ -117,7 +117,7 @@ def _extract_identity_id(self, token_response: OAuthTokenResponse) -> str | None class FileTokenStorage(TokenStorage, metaclass=abc.ABCMeta): """ - A base class for token storages which store tokens on a local file. + A base class for token storages which store tokens in a local file. Common functionality for file-based token storages like file creation and class instantiation for a GlobusApp is defined here. diff --git a/src/globus_sdk/tokenstorage/v2/json.py b/src/globus_sdk/tokenstorage/v2/json.py index 9ecb9f88..122d8fb8 100644 --- a/src/globus_sdk/tokenstorage/v2/json.py +++ b/src/globus_sdk/tokenstorage/v2/json.py @@ -21,7 +21,7 @@ class _JSONFileData(_JSONFileData_0): # pylint: disable=inherit-non-class class JSONTokenStorage(FileTokenStorage): """ - A token storage which stores token data on disk in json files. + A token storage which stores token data on disk in a JSON file. This class defines a `format_version` which determines what the specific data shape. Any data in a `supported_version` format which is not the primary `format_version` @@ -32,7 +32,7 @@ class JSONTokenStorage(FileTokenStorage): :cvar "2.0" format_version: The data format version used when writing data. :cvar ("1.0", "2.0") supported_versions: The list of data format versions which can be read. - :param filepath: The path to a json file where token data should be stored. + :param filepath: The path to a JSON file where token data should be stored. :param namespace: A unique string for partitioning token data (Default: "DEFAULT"). """ @@ -124,16 +124,17 @@ def store_token_data_by_resource_server( self, token_data_by_resource_server: t.Mapping[str, TokenStorageData] ) -> None: """ - Store token data for one or more resource server in the current namespace + Store token data for one or more resource server in the current namespace. - Token data, alongside globus-sdk version info, is serialized to json before + Token data, alongside Globus SDK version info, is serialized to JSON before being written to the file at ``self.filepath``. Under the assumption that this may be running on a system with multiple local users, this sets the umask such that only the owner of the resulting file can read or write it. - :param token_data_by_resource_server: mapping of resource server to token data. + :param token_data_by_resource_server: A mapping of resource servers to token + data. """ to_write = self._load() diff --git a/src/globus_sdk/tokenstorage/v2/sqlite.py b/src/globus_sdk/tokenstorage/v2/sqlite.py index bacd5662..2f4b7c8d 100644 --- a/src/globus_sdk/tokenstorage/v2/sqlite.py +++ b/src/globus_sdk/tokenstorage/v2/sqlite.py @@ -15,7 +15,7 @@ class SQLiteTokenStorage(FileTokenStorage): """ - A token storage which stores token data on disk in sqlite databases. + A token storage which stores token data on disk in a SQLite database. See :class:`TokenStorage` for common interface details. @@ -105,11 +105,12 @@ def store_token_data_by_resource_server( self, token_data_by_resource_server: t.Mapping[str, TokenStorageData] ) -> None: """ - Store token data for one or more resource server in the current namespace. + Store token data for one or more resource servers in the current namespace. - Token data is json-serialized before being inserted into the database. + Token data is JSON-serialized before being inserted into the database. - :param token_data_by_resource_server: mapping of resource server to token data. + :param token_data_by_resource_server: A mapping of resource servers to token + data. """ pairs = [] for resource_server, token_data in token_data_by_resource_server.items(): @@ -129,7 +130,7 @@ def get_token_data_by_resource_server(self) -> dict[str, TokenStorageData]: """ Lookup all token data under the current namespace from the database. - :returns: a dict of ``TokenStorageData`` objects indexed by their + :returns: A dict of ``TokenStorageData`` objects indexed by their resource server. """ ret: dict[str, TokenStorageData] = {} @@ -161,7 +162,7 @@ def remove_token_data(self, resource_server: str) -> bool: return rowcount != 0 def iter_namespaces(self) -> t.Iterator[str]: - """Iterate over all distinct namespaces in the sqlite database.""" + """Iterate over all distinct namespaces in the SQLite database.""" seen: set[str] = set() for row in self._connection.execute( "SELECT DISTINCT namespace FROM token_storage;" diff --git a/src/globus_sdk/tokenstorage/v2/token_data.py b/src/globus_sdk/tokenstorage/v2/token_data.py index 76fa97ec..1ddf106d 100644 --- a/src/globus_sdk/tokenstorage/v2/token_data.py +++ b/src/globus_sdk/tokenstorage/v2/token_data.py @@ -14,8 +14,9 @@ class TokenStorageData(Serializable): Tokens are scoped to a specific user/client (`identity_id`) performing specific operations (`scope`) with a specific service (`resource_server`). - :ivar str resource_server: The resource server this for which this token data was + :ivar str resource_server: The resource server for which this token data was granted. + :ivar str identity_id: The primary identity id of the user or client which requested this token. This will be None if an identity id was not extractable from the token grant response. @@ -23,10 +24,10 @@ class TokenStorageData(Serializable): :ivar str scope: A space separated list of scopes that this token data provides access to. - :ivar str access_token: A globus auth issued OAuth2 access token. Used for + :ivar str access_token: A Globus Auth-issued OAuth2 access token. Used for authentication when interacting with service APIs. - :ivar str | None refresh_token: A globus auth issued OAuth2 refresh token. Used to + :ivar str | None refresh_token: A Globus Auth-issued OAuth2 refresh token. Used to obtain new access tokens when the current one expires. This value will be None if the original token grant did not request refresh tokens. @@ -34,7 +35,7 @@ class TokenStorageData(Serializable): access_token expires. :ivar str | None token_type: The token type of access_token, currently this will - always be "Bearer" if present + always be "Bearer" if present. :param extra: An optional dictionary of additional fields to include. Included for forward/backward compatibility.