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

Promote gare from experimental #1048

Merged
merged 5 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions changelog.d/20240916_102235_sirosen_move_consents.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Changed
~~~~~~~

- Globus Auth Requirements errors are no longer ``experimental``. They have
been moved to the ``globus_sdk.gare`` module and the primary document type
has been renamed from ``GlobusAuthRequirementsError`` to ``GARE``. (:pr:`NUMBER`)
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
Auth Requirements Errors
========================

Globus Auth Requirements Error is a response format that conveys to a client any
'Globus Auth Requirements Error' is a response format that conveys to a client any
sirosen marked this conversation as resolved.
Show resolved Hide resolved
modifications to a session (i.e., "boosting") that will be required
to complete a particular request.

The ``globus_sdk.experimental.auth_requirements_error`` module provides a
number of tools to make it easier to identify and handle these errors when they occur.
The ``globus_sdk.gare`` module provides a number of tools to make it easier to
identify and handle these errors when they occur.

GlobusAuthRequirementsError
---------------------------
GARE
----

The ``GlobusAuthRequirementsError`` class provides a model for working with Globus
Auth Requirements Error responses.
The ``GARE`` class provides a model for working with Globus Auth Requirements Error
responses.

Services in the Globus ecosystem may need to communicate authorization requirements
to their consumers. For example, a service may need to instruct clients to have the user
consent to an additional scope, ``"foo"``. In such a case, ``GlobusAuthRequirementsError``
can provide serialization into the well-known Globus Auth Requirements Error format:
consent to an additional scope, ``"foo"``. In such a case, ``GARE`` can provide
serialization into the well-known Globus Auth Requirements Error format:

.. code-block:: python

from globus_sdk.experimental.auth_requirements_error import GlobusAuthRequirementsError
from globus_sdk.gare import GARE

error = GlobusAuthRequirementsError(
error_doc = GARE(
code="ConsentRequired",
authorization_parameters=GlobusAuthorizationParameters(
required_scopes=["foo"],
Expand All @@ -42,9 +42,9 @@ by specifying ``include_extra=True`` when calling ``to_dict()``.

.. code-block:: python

from globus_sdk.experimental.auth_requirements_error import GlobusAuthRequirementsError
from globus_sdk.gare import GARE

error = GlobusAuthRequirementsError(
error = GARE(
code="ConsentRequired",
authorization_parameters=GlobusAuthorizationParameters(
required_scopes=["foo"],
Expand All @@ -61,8 +61,8 @@ by specifying ``include_extra=True`` when calling ``to_dict()``.
# Render a dictionary with extra fields
error.to_dict(include_extra=True)

These fields are stored by both the ``GlobusAuthRequirementsError`` and
``GlobusAuthenticationParameters`` classes in an ``extra`` attribute.
These fields are stored by both the ``GARE`` and ``GlobusAuthenticationParameters``
classes in an ``extra`` attribute.

.. note::

Expand All @@ -75,37 +75,34 @@ These fields are stored by both the ``GlobusAuthRequirementsError`` and
Parsing Responses
-----------------

If you are writing a client to a Globus API, the ``auth_requirements_error`` subpackage
provides utilities to detect legacy Globus Auth requirements error response
formats and normalize them.
If you are writing a client to a Globus API, the ``gare`` subpackage provides utilities
to detect legacy Globus Auth requirements error response formats and normalize them.

To detect if a ``GlobusAPIError``, ``ErrorSubdocument``, or JSON response
dictionary represents an error that can be converted to a Globus Auth
Requirements Error, you can use, e.g.,:

.. code-block:: python

from globus_sdk.experimental import auth_requirements_error
from globus_sdk import gare

error_dict = {
"code": "ConsentRequired",
"message": "Missing required foo consent",
}
# The dict is not a Globus Auth Requirements Error, so `False` is returned.
auth_requirements_error.utils.is_auth_requirements_error(error_dict)
gare.is_auth_requirements_error(error_dict)

# The dict is not a Globus Auth Requirements Error and cannot be converted.
auth_requirements_error.utils.to_auth_requirements_error(error_dict) # None
gare.to_auth_requirements_error(error_dict) # None

error_dict = {
"code": "ConsentRequired",
"message": "Missing required foo consent",
"required_scopes": ["urn:globus:auth:scope:transfer.api.globus.org:all[*foo]"],
}
auth_requirements_error.utils.is_auth_requirements_error(error_dict) # True
auth_requirements_error.utils.to_auth_requirements_error(
error_dict
) # GlobusAuthRequirementsError
gare.is_auth_requirements_error(error_dict) # True
gare.to_auth_requirements_error(error_dict) # GARE

.. note::

Expand All @@ -118,22 +115,38 @@ Requirements Error, you can use, e.g.,:

.. code-block:: python

auth_requirements_error.utils.to_auth_requirements_error(
other_error
) # GlobusAuthRequirementsError
auth_requirements_error.utils.to_auth_requirements_errors(
[other_error]
) # [GlobusAuthRequirementsError, ...]
gare.to_auth_requirements_error(other_error) # GARE
gare.to_auth_requirements_errors([other_error]) # [GARE, ...]
sirosen marked this conversation as resolved.
Show resolved Hide resolved

Notes
-----

``GlobusAuthRequirementsError`` enforces types strictly when parsing a Globus
Auth Requirements Error response dictionary, and will raise a ``ValueError`` if a
``GARE`` enforces types strictly when parsing a Globus Auth Requirements Error
response dictionary, and will raise a :class:`globus_sdk.ValidationError` if a
supported field is supplied with a value of the wrong type.

``GlobusAuthRequirementsError`` does not attempt to mimic or itself enforce
any logic specific to the Globus Auth service with regard to what represents a valid
combination of fields (e.g., ``session_required_mfa`` requires either
``session_required_identities`` or ``session_required_single_domain``
in order to be properly handled).
``GARE`` does not attempt to mimic or itself enforce any logic specific to the
Globus Auth service with regard to what represents a valid combination of fields
(e.g., ``session_required_mfa`` requires either ``session_required_identities`` or
``session_required_single_domain`` in order to be properly handled).

Reference
---------

.. currentmodule:: globus_sdk.gare

.. autoclass:: GARE
:members:
:inherited-members:

.. autoclass:: GlobusAuthorizationParameters
:members:
:inherited-members:

.. autofunction:: to_auth_requirements_error

.. autofunction:: to_auth_requirements_errors

.. autofunction:: is_auth_requirements_error

.. autofunction:: has_auth_requirements_errors
1 change: 1 addition & 0 deletions docs/authorization/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ Components of the Globus SDK which handle application authorization.

globus_authorizers
scopes_and_consents/index
gare
1 change: 0 additions & 1 deletion docs/experimental/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ Globus SDK Experimental Components
:caption: Contents
:maxdepth: 1

auth_requirements_errors
scope_parser
consents
globus_app
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ dependencies = [
"cryptography>=3.3.1,!=3.4.0",
# depend on the latest version of typing-extensions on python versions which do
# not have all of the typing features we use
'typing_extensions>=4.0; python_version<"3.10"',
'typing_extensions>=4.0; python_version<"3.11"',
# python versions older than 3.9 don't have importlib.resources
'importlib_resources>=5.12.0; python_version<"3.9"',
]
Expand Down
8 changes: 6 additions & 2 deletions src/globus_sdk/_serializable.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
from __future__ import annotations

import inspect
import sys
import typing as t

T = t.TypeVar("T", bound="Serializable")
if sys.version_info >= (3, 11):
from typing import Self
else:
from typing_extensions import Self


class Serializable:
Expand All @@ -30,7 +34,7 @@ def _supported_fields(cls) -> list[str]:
]

@classmethod
def from_dict(cls: type[T], data: dict[str, t.Any]) -> T:
def from_dict(cls, data: dict[str, t.Any]) -> Self:
"""
Instantiate from a dictionary.

Expand Down
46 changes: 46 additions & 0 deletions src/globus_sdk/experimental/auth_requirements_error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from __future__ import annotations

import sys
import typing as t

__all__ = (
"GlobusAuthRequirementsError",
"GlobusAuthorizationParameters",
"to_auth_requirements_error",
"to_auth_requirements_errors",
"is_auth_requirements_error",
"has_auth_requirements_errors",
)

# legacy aliases
# (when accessed, these will emit deprecation warnings)
if t.TYPE_CHECKING:
from globus_sdk.gare import GARE as GlobusAuthRequirementsError
from globus_sdk.gare import (
GlobusAuthorizationParameters,
has_auth_requirements_errors,
is_auth_requirements_error,
to_auth_requirements_error,
to_auth_requirements_errors,
)
else:

def __getattr__(name: str) -> t.Any:
import globus_sdk.gare as gare_module
from globus_sdk.exc import warn_deprecated

warn_deprecated(
"'globus_sdk.experimental.auth_requirements_error' has been renamed to "
"'globus_sdk.gare'. "
f"Importing '{name}' from `globus_sdk.experimental` is deprecated."
)

# rename GlobusAuthRequirementsError -> GARE
if name == "GlobusAuthRequirementsError":
name = "GARE"

value = getattr(gare_module, name, None)
if value is None:
raise AttributeError(f"module {__name__} has no attribute {name}")
setattr(sys.modules[__name__], name, value)
return value
4 changes: 1 addition & 3 deletions src/globus_sdk/experimental/globus_app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@
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.auth_requirements_error import (
GlobusAuthorizationParameters,
)
from globus_sdk.experimental.tokenstorage import TokenStorage
from globus_sdk.gare import GlobusAuthorizationParameters
from globus_sdk.scopes import AuthScopes, scopes_to_scope_list

from ._types import TokenStorageProvider
Expand Down
4 changes: 1 addition & 3 deletions src/globus_sdk/experimental/globus_app/client_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

from globus_sdk import AuthLoginClient, ConfidentialAppAuthClient, GlobusSDKUsageError
from globus_sdk._types import ScopeCollectionType, UUIDLike
from globus_sdk.experimental.auth_requirements_error import (
GlobusAuthorizationParameters,
)
from globus_sdk.gare import GlobusAuthorizationParameters

from .app import GlobusApp
from .authorizer_factory import ClientCredentialsAuthorizerFactory
Expand Down
4 changes: 1 addition & 3 deletions src/globus_sdk/experimental/globus_app/user_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@
NativeAppAuthClient,
)
from globus_sdk._types import ScopeCollectionType, UUIDLike
from globus_sdk.experimental.auth_requirements_error import (
GlobusAuthorizationParameters,
)
from globus_sdk.experimental.login_flow_manager import (
CommandLineLoginFlowManager,
LoginFlowManager,
)
from globus_sdk.gare import GlobusAuthorizationParameters

from ._types import LoginFlowManagerProvider
from .app import GlobusApp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
GlobusSDKUsageError,
OAuthTokenResponse,
)
from globus_sdk.experimental.auth_requirements_error import (
GlobusAuthorizationParameters,
)
from globus_sdk.gare import GlobusAuthorizationParameters

from .login_flow_manager import LoginFlowManager

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@
from string import Template

from globus_sdk import AuthLoginClient, GlobusSDKUsageError, OAuthTokenResponse
from globus_sdk.experimental.auth_requirements_error import (
GlobusAuthorizationParameters,
)
from globus_sdk.experimental.login_flow_manager.login_flow_manager import (
LoginFlowManager,
)
from globus_sdk.gare import GlobusAuthorizationParameters

from ._local_server import (
DEFAULT_HTML_TEMPLATE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
NativeAppAuthClient,
OAuthTokenResponse,
)
from globus_sdk.experimental.auth_requirements_error import (
GlobusAuthorizationParameters,
)
from globus_sdk.gare import GlobusAuthorizationParameters


class LoginFlowManager(metaclass=abc.ABCMeta):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
from ._auth_requirements_error import (
GlobusAuthorizationParameters,
GlobusAuthRequirementsError,
)
from ._auth_requirements_error import GARE, GlobusAuthorizationParameters
from ._functional_api import (
has_auth_requirements_errors,
is_auth_requirements_error,
Expand All @@ -10,7 +7,7 @@
)

__all__ = [
"GlobusAuthRequirementsError",
"GARE",
"GlobusAuthorizationParameters",
"to_auth_requirements_error",
"to_auth_requirements_errors",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ class GlobusAuthorizationParameters(Serializable):
Data class containing authorization parameters that can be passed during
an authentication flow to control how the user will authenticate.

When used with a GlobusAuthRequirementsError this represents the additional
authorization parameters needed in order to complete a request that had
insufficient authorization state.
When used with a GARE this represents the additional authorization
sirosen marked this conversation as resolved.
Show resolved Hide resolved
parameters needed in order to complete a request that had insufficient
authorization state.

:ivar session_message: A message to be displayed to the user.
:vartype session_message: str, optional
Expand Down Expand Up @@ -77,7 +77,7 @@ def __init__(
self.extra = extra or {}


class GlobusAuthRequirementsError(Serializable):
class GARE(Serializable):
"""
Represents a Globus Auth Requirements Error.

Expand Down
Loading