Skip to content

Commit

Permalink
Refactor Authorization docs (leaving stub pages behind) (#1046)
Browse files Browse the repository at this point in the history
* Move authorizer documentation into authorization/

- Move `authorization.rst` to `authorization/globus_authorizers.rst`
- Create a stub at `authorization.rst` (orphan doc) which directs
  users to the new page
- Introduce `authorization/index.rst` with a single-item TOC

* Move scope docs into `authorization/`

- Move `scopes.rst` to `authorization/scopes_and_consents/scopes.rst`
- Add `scopes.rst` as a stub, pointing to the new page
- Reduce `experimental/scope_parser.rst` to a stub (was full doc!)
- Move `MutableScope` docs to a separate page in
  `scopes_and_consents/` and note it as deprecated (doc note only)
- Update `MutableScope` usage docs to use `Scope` instead
- Update toctrees to include the new docs

* Apply suggestions from code review

Co-authored-by: derek-globus <113056046+derek-globus@users.noreply.github.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Changes per review

* Apply suggestions from code review

Co-authored-by: Ada <107940310+ada-globus@users.noreply.github.com>

---------

Co-authored-by: derek-globus <113056046+derek-globus@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Ada <107940310+ada-globus@users.noreply.github.com>
  • Loading branch information
4 people authored Sep 14, 2024
1 parent 54723e4 commit 0c892dd
Show file tree
Hide file tree
Showing 11 changed files with 488 additions and 330 deletions.
87 changes: 3 additions & 84 deletions docs/authorization.rst
Original file line number Diff line number Diff line change
@@ -1,85 +1,4 @@
.. _authorization:
:orphan:

API Authorization
=================

.. currentmodule:: globus_sdk.authorizers

Authorizing calls against Globus can be a complex process.
In particular, if you are using Refresh Tokens and short-lived Access Tokens,
you may need to take particular care managing your Authorization state.

Within the SDK, we solve this problem by using :class:`GlobusAuthorizer`\s,
which are attached to clients. A :class:`GlobusAuthorizer` is an object which
defines the following two operations:

- get an ``Authorization`` header
- handle a 401 Unauthorized error

Whenever using the :ref:`Service Clients <services>`, you should be passing in an
authorizer when you create a new client unless otherwise specified.

The type of authorizer you will use depends very much on your application, but
if you want examples you should look at the :ref:`examples section
<examples-authorization>`.
It may help to start with the examples and come back to the full documentation
afterwards.

The Authorizer Interface
------------------------

We define the interface for ``GlobusAuthorizer`` objects in terms of an
Abstract Base Class:

.. autoclass:: GlobusAuthorizer
:members:
:member-order: bysource

``GlobusAuthorizer`` objects that fetch new access tokens when their existing
ones expire or a 401 is received implement the RenewingAuthorizer class

.. autoclass:: RenewingAuthorizer
:members: get_authorization_header, handle_missing_authorization
:member-order: bysource
:show-inheritance:

``GlobusAuthorizer`` objects which have a static authorization header are all
implemented using the static authorizer class:

.. autoclass:: StaticGlobusAuthorizer
:members:
:member-order: bysource
:show-inheritance:

Authorizer Types
----------------

.. currentmodule:: globus_sdk

All of these types of authorizers can be imported from
``globus_sdk.authorizers``.

.. autoclass:: NullAuthorizer
:members:
:member-order: bysource
:show-inheritance:

.. autoclass:: BasicAuthorizer
:members:
:member-order: bysource
:show-inheritance:

.. autoclass:: AccessTokenAuthorizer
:members:
:member-order: bysource
:show-inheritance:

.. autoclass:: RefreshTokenAuthorizer
:members:
:member-order: bysource
:show-inheritance:

.. autoclass:: ClientCredentialsAuthorizer
:members:
:member-order: bysource
:show-inheritance:
The documentation which was found on this page has moved to
:ref:`Globus Authorizers <globus_authorizers>`.
93 changes: 93 additions & 0 deletions docs/authorization/globus_authorizers.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
.. _globus_authorizers:

Globus Authorizers
==================

.. currentmodule:: globus_sdk.authorizers

Globus SDK clients need credentials (tokens) to authenticate against services
and authorize their various API calls. Using Globus Auth, the Globus OAuth2
service, clients can be provided with credentials which are either static or
dynamic.

The interface used to handle these credentials is :class:`GlobusAuthorizer`.
:class:`GlobusAuthorizer` defines methods which provide an ``Authorization``
header for HTTP requests, and different implementations handle Refresh Tokens,
Access Tokens, Client Credentials, and their various usage modes.

A :class:`GlobusAuthorizer` is an object which defines the following two
operations:

- get an ``Authorization`` header
- handle a 401 Unauthorized error

Clients contain authorizers, and use them to get and refresh their credentials.
Whenever using the :ref:`Service Clients <services>`, you should be passing in an
authorizer when you create a new client unless otherwise specified.

The type of authorizer you will use depends very much on your application, but
if you want examples you should look at the :ref:`examples section
<examples-authorization>`.
It may help to start with the examples and come back to the reference
documentation afterwards.

The Authorizer Interface
------------------------

We define the interface for ``GlobusAuthorizer`` objects in terms of an
Abstract Base Class:

.. autoclass:: GlobusAuthorizer
:members:
:member-order: bysource

Authorizers within this SDK fall into two categories:

* "Static Authorizers" already contain all authorization data and simply format it
into the proper authorization header.
These all inherit from the ``StaticGlobusAuthorizer`` class.

* "Renewing Authorizer" take some initial parameters but internally define a
functional behavior to acquire new authorization data as necessary.
These all inherit from the ``RenewingGlobusAuthorizer`` class.

.. autoclass:: StaticGlobusAuthorizer
:member-order: bysource
:show-inheritance:

.. autoclass:: RenewingAuthorizer
:member-order: bysource
:show-inheritance:

Authorizer Types
----------------

.. currentmodule:: globus_sdk

All of these types of authorizers can be imported from
``globus_sdk.authorizers``.

.. autoclass:: AccessTokenAuthorizer
:members:
:member-order: bysource
:show-inheritance:

.. autoclass:: RefreshTokenAuthorizer
:members:
:member-order: bysource
:show-inheritance:

.. autoclass:: ClientCredentialsAuthorizer
:members:
:member-order: bysource
:show-inheritance:

.. autoclass:: BasicAuthorizer
:members:
:member-order: bysource
:show-inheritance:

.. autoclass:: NullAuthorizer
:members:
:member-order: bysource
:show-inheritance:
10 changes: 10 additions & 0 deletions docs/authorization/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Globus SDK Authorization
========================

Components of the Globus SDK which handle application authorization.

.. toctree::
:maxdepth: 1

globus_authorizers
scopes_and_consents/index
33 changes: 33 additions & 0 deletions docs/authorization/scopes_and_consents/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Scopes and Consents
===================

Globus uses OAuth2 scopes to control access to different APIs and allow
applications provide a least-privilege security guarantee to their users.

Overview
--------

A "Consent" is a record, in Globus Auth, that an application is allowed to take
some action on behalf of a user. A "Scope" is the name of some action or set of
actions.

For example, ``urn:globus:auth:scope:groups.api.globus.org:all`` is a scope
which gives full access to Globus Groups. Users _consent_ to allow applications,
like the Globus CLI, to get credentials which are "valid for this scope", and
thereby to view and manipulate their group memberships.

For more information, see
`this docs.globus.org explanation of scopes and consents
<https://docs.globus.org/guides/overviews/clients-scopes-and-consents/>`_.

Reference
---------

Within the Globus SDK, Scopes and Consents are modeled using several objects
which make learning about and manipulating these data easier.

.. toctree::
:maxdepth: 1

scopes
mutable_scopes
95 changes: 95 additions & 0 deletions docs/authorization/scopes_and_consents/mutable_scopes.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
.. _mutable_scopes:

.. currentmodule:: globus_sdk.scopes

MutableScopes
=============

.. warning::

The ``MutableScope`` class and its interfaces are considered a legacy
feature. They will be deprecated and removed in a future SDK release.

Users should prefer to use ``globus_sdk.Scope`` instead.
``globus_sdk.Scope``, documented in :ref:`the scopes documentation <scopes>`,
provides strictly more features and has a superior interface.

In order to support optional and dependent scopes, a type is
provided by ``globus_sdk.scopes``: the ``MutableScope`` class.

``MutableScope`` can be constructed directly or via a ``ScopeBuilder``'s
``make_mutable`` method, given a scope's short name.

For example, one can create a ``MutableScope`` from the Groups "all" scope
as follows:

.. code-block:: python
from globus_sdk.scopes import GroupsScopes
scope = GroupsScopes.make_mutable("all")
``MutableScope`` objects primarily provide two main pieces of functionality:
dynamically building a scope tree and serializing to a string.

Dynamic Scope Construction
~~~~~~~~~~~~~~~~~~~~~~~~~~

``MutableScope`` objects provide a tree-like interface for constructing scopes
and their dependencies.

For example, the transfer scope dependent upon a collection scope may be
constructed by means of ``MutableScope`` methods and the ``make_mutable`` method
of scope builders thusly:

.. code-block:: python
from globus_sdk.scopes import GCSCollectionScopeBuilder, TransferScopes
MAPPED_COLLECTION_ID = "...ID HERE..."
# create the scopes with make_mutable
transfer_scope = TransferScopes.make_mutable("all")
data_access_scope = GCSCollectionScopeBuilder(MAPPED_COLLECTION_ID).make_mutable(
"data_access", optional=True
)
# add data_access as a dependency
transfer_scope.add_dependency(data_access_scope)
``MutableScope``\s can be used in most of the same locations where scope
strings can be used, but you can also call ``str()`` on them to get a
stringified representation.

Serializing Scopes
~~~~~~~~~~~~~~~~~~

Whenever scopes are being sent to Globus services, they need to be encoded as
strings. All mutable scope objects support this by means of their defined
``serialize`` method. Note that ``__str__`` for a ``MutableScope`` is just an
alias for ``serialize``. For example, the following is valid usage to demonstrate
``str()``, ``repr()``, and ``serialize()``:

.. code-block:: pycon
>>> from globus_sdk.scopes import MutableScope
>>> foo = MutableScope("foo")
>>> bar = MutableScope("bar")
>>> bar.add_dependency("baz")
>>> foo.add_dependency(bar)
>>> print(str(foo))
foo[bar[baz]]
>>> print(bar.serialize())
bar[baz]
>>> alpha = MutableScope("alpha")
>>> alpha.add_dependency(MutableScope("beta", optional=True))
>>> print(str(alpha))
alpha[*beta]
>>> print(repr(alpha))
MutableScope("alpha", dependencies=[MutableScope("beta", optional=True)])
MutableScope Reference
~~~~~~~~~~~~~~~~~~~~~~

.. autoclass:: MutableScope
:members:
:show-inheritance:
Loading

0 comments on commit 0c892dd

Please sign in to comment.