File: gare.rst

package info (click to toggle)
python-globus-sdk 4.3.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,172 kB
  • sloc: python: 35,227; sh: 44; makefile: 35
file content (192 lines) | stat: -rw-r--r-- 6,811 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
.. _gares:

Globus Auth Requirements Errors (GAREs)
=======================================

'Globus Auth Requirements Error' is a response format that conveys to a client any
modifications to a session (i.e., "boosting") that will be required
to complete a particular request.

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

GARE
----

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, ``GARE`` can provide
serialization into the well-known Globus Auth Requirements Error format:

.. code-block:: python

    from globus_sdk.gare import GARE

    error_doc = GARE(
        code="ConsentRequired",
        authorization_parameters=GlobusAuthorizationParameters(
            required_scopes=["foo"],
            session_message="Missing required 'foo' consent",
        ),
    )

    # Render a strict dictionary
    error.to_dict()

If non-canonical fields are needed, the ``extra`` argument can be used to
supply a dictionary of additional fields to include. Non-canonical fields present
in the provided dictionary when calling ``from_dict()`` are stored similarly.
You can include these fields in the rendered output dictionary
by specifying ``include_extra=True`` when calling ``to_dict()``.

.. code-block:: python

    from globus_sdk.gare import GARE

    error = GARE(
        code="ConsentRequired",
        authorization_parameters=GlobusAuthorizationParameters(
            required_scopes=["foo"],
            session_message="Missing required 'foo' consent",
        ),
        extra={
            "message": "Missing required 'foo' consent",
            "request_id": "WmMV97A1w",
            "required_scopes": ["foo"],
            "resource": "/transfer",
        },
    )

    # Render a dictionary with extra fields
    error.to_dict(include_extra=True)

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

.. note::

    Non-canonical fields in a Globus Auth Requirements Error are primarily intended
    to make it easier for services to provide backward-compatibile error responses
    to clients that have not adopted the Globus Auth Requirements Error format. Avoid
    using non-canonical fields for any data that should be generically understood by
    a consumer of the error response.

.. _globus_app_gare_integration:

GlobusApp Integration
---------------------

In order to standardize a common GARE-handling approach, the SDK's
:ref:`GlobusApp <globus_apps>` contains a built-in GARE redrive mechanism (disabled
by default).

When enabled (setting the config flag ``auto_redrive_gares`` to ``True``), any
client created with a GlobusApp will receive an additional request-transport retry
handler. This handler will intercept the first occurrence of a 403 response which
parses as a GARE and "redrive it", by:

1. Initiating a fresh login request with the defined ``LoginFlowManager``
2. Caching new tokens for subsequent requests
3. Re-attempting the original request with the new tokens

This allows for very simple error-handling, particularly in scripts, wherein the
executing user or client is automatically prompted to remediate manually-required
steps (e.g., consent, MFA) without any additional code.


.. code-block:: python

    from globus_sdk import UserApp, GlobusAppConfig, TransferClient

    config = GlobusAppConfig(auto_redrive_gares=True)
    with UserApp("my-gare-demo", "<client-id>", config=config) as app:
        tc = TransferClient(app=app)

        # If the transfer service were to return a 403 GARE, the script runner would be
        # prompted to log in with any explicit specifications, e.g., MFA, consents.
        # Once they complete the login flow (by providing the newly minted auth code),
        # the original request is attempted once more.
        task = tc.submit_transfer(your_transfer_data)


Parsing Responses
-----------------

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 import gare

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

    # The dict is not a Globus Auth Requirements Error and cannot be converted.
    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]"],
    }
    gare.is_auth_requirements_error(error_dict)  # True
    gare.to_auth_requirements_error(error_dict)  # GARE

.. note::

    If a ``GlobusAPIError`` represents multiple errors that were returned in an
    array, ``to_auth_requirements_error()`` only returns the first error in that
    array that can be converted to the Globus Auth Requirements Error response format.
    In this case (and in general) it's preferable to use
    ``to_auth_requirements_errors()`` (which also accepts a list of
    ``GlobusAPIError``\ s, ``ErrorSubdocument``\ s, and JSON response dictionaries):

.. code-block:: python

    gare.to_auth_requirements_error(other_error)  # GARE
    gare.to_auth_requirements_errors([other_error])  # [GARE, ...]

Notes
-----

``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.

``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_gare

.. autofunction:: to_gares

.. autofunction:: is_gare

.. autofunction:: has_gares