Skip to content

Commit

Permalink
Merge pull request #28 from djpugh/feature/user-klass-docs
Browse files Browse the repository at this point in the history
  • Loading branch information
djpugh authored Dec 6, 2020
2 parents fae25c7 + 23662dc commit 78e2f94
Show file tree
Hide file tree
Showing 23 changed files with 299 additions and 34 deletions.
53 changes: 53 additions & 0 deletions docs/source/advanced.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
Using fastapi_aad_auth
**********************
Please see `Basic Usage <usage>`_ for information on how to configure and setup ``fastapi_aad_auth``.

Accessing User Tokens/View
~~~~~~~~~~~~~~~~~~~~~~~~~~

There are two routes that are automatically added to this, the ``/me`` and ``/me/getToken`` routes. The ``/me`` route provides a summary of the current user, and enables them to get a bearer token from Azure AD.
The ``/me/token`` endpoint provides that same token (for the logged in user) in a JSON object

.. warning::

To get the token, this is primarily an interactive method, as it requires caching the token through the UI session based login approach, so it can fail intermittently depending on if the user has logged in recently.

This can be disabled by setting the ``config.routing.user_path`` to ``None`` or ``''``. #

Customising the User Model
~~~~~~~~~~~~~~~~~~~~~~~~~~

The authentication state user can be processed within the application methods - the ``Depends`` part of the api route returns an
:class:`~fastapi_aad_auth.oauth.state.AuthenticationState` object - ``auth_state`` in the ``testapp`` (see :ref:`testing`).

.. literalinclude:: ../../tests/testapp/server.py
:language: python
:linenos:
:start-at: @router.get('/hello')
:end-at: return

The associated user is then available at ``auth_state.user``

The :class:`~fastapi_aad_auth.oauth.aad.AADOAuthBackend` object takes a ``user_klass`` argument:

.. literalinclude:: ../../src/fastapi_aad_auth/oauth/aad.py
:language: python
:linenos:
:start-at: class AADOAuthBackend
:end-before: """Initialise

which defaults to the really basic :class:`~fastapi_aad_auth.oauth.state.User` class, but any object with the same
interface should work, so you can add e.g. database calls etc. to validate/persist/check the user and any other
desired behaviours.

You can customise this when initialising the :class:`~fastapi_aad_auth.auth.AADAuth` object by setting
the :class:`~fastapi_aad_auth.config.Config` ``user_klass`` variable (this can also be done by the
associated environment variable)::

from fastapi_aad_auth import AADAuth, Config

config = Config()
config.user_klass = MyUserClass

auth = AADAuth(config)

85 changes: 84 additions & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@

import os
import datetime as dt
from pathlib import Path
from typing import Any
import uuid

from pydantic import BaseModel, SecretStr
import sphinx_material
from sphinx.ext.autodoc import ALL, ClassDocumenter


from fastapi_aad_auth import __version__
Expand Down Expand Up @@ -157,7 +162,7 @@
'globaltoc_includehidden': False,
"logo_icon": "lock",
"repo_type": "github",
"globaltoc_depth": 2,
# "globaltoc_depth": 2,
"color_primary": "cyan",
"color_accent": "teal",
"touch_icon": "images/apple-icon-152x152.png",
Expand Down Expand Up @@ -432,6 +437,84 @@
intersphinx_mapping = {'https://docs.python.org/': None}


class ConfigDocumenter(ClassDocumenter):

objtype = 'config'
directivetype = 'data'
domain = 'py'
priority = -1000

def generate(self, *args, **kwargs) -> None:
"""Generate reST for the object given by *self.name*, and possibly for
its members.
If *more_content* is given, include that content. If *real_modname* is
given, use that module name to find attribute docs. If *check_module* is
True, only generate if the object is defined in the module name it is
imported from. If *all_members* is True, document all members.
"""
if not self.parse_name():
return
# now, import the module and get object to document
if not self.import_object():
return
sourcename = self.get_sourcename()
config_vars = self._process_variables(self.object, '<Config>')

# e.g. the module directive doesn't have content
self.indent = ''
# add all content (from docstrings, attribute docs etc.)
self.analyzer = None
self.add_directive_header('')
self.add_line('**Options:**', sourcename)
for line in config_vars:
self.add_line(line, sourcename)

@classmethod
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
) -> bool:
return hasattr(member, '__mro__') and BaseModel in member.__mro__

def _process_variables(self, object, path):
config_vars = []
config_nested = {}
for field_name, field in object.__fields__.items():
if BaseModel in field.type_.__mro__:
# This is recursive here
config_nested[field_name] = self._process_variables(field.type_, f'{path}.{field_name}')
else:
default_str = ''
if field.default:
if not SecretStr in field.type_.__mro__:
if Path in field.type_.__mro__:
field.default = Path(field.default).relative_to(Path(field.default).parents[2])
if field_name == 'user_klass':
default_str = f' [default: :class:`{field.default.replace("`", "").replace(":", ".")}`]'
else:
default_str = f' [default: ``{field.default}``]'
else:
default_str = ' [default: ``uuid.uuid4()``]'
module = field.outer_type_.__module__
if module != 'builtins':
if hasattr(field.outer_type_, '__origin__'):
type_ = f' (:class:`{field.outer_type_.__origin__.__name__}`) '
elif not hasattr(field.outer_type_, '__name__'):
type_ = ''
else:
type_ = f' (:class:`{module}.{field.outer_type_.__name__}`) '
else:
type_ = f' (``{field.outer_type_.__name__}``) '
env_var = ''
if 'env' in field.field_info.extra:
env_var = f' (Can be set by ``{field.field_info.extra["env"]}`` environment variable)'
config_vars.append(f' * ``{path}.{field_name}``{type_}: {field.field_info.description}{default_str}{env_var}')
for field_name in sorted(config_nested.keys()):
config_vars.append(f' ``{path}.{field_name}``:')
for var in config_nested[field_name]:
config_vars.append(f' {var}')
return config_vars


def setup(app):
from sphinx.util.texescape import tex_replacements
tex_replacements += [(u'£', u"\\")]
app.add_autodocumenter(ConfigDocumenter)
10 changes: 4 additions & 6 deletions docs/source/config.rst
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
Configuration Options
*********************

Many of the configuration options can be set using environment variables (or a ``.env`` file).

.. contents:: Table of Contents
:depth: 2
This section describes the overall configuration object and it's (nested) options, defined in:


.. automodule:: fastapi_aad_auth.config
:members:
:exclude-members: bool_from_env,list_from_env
.. autoconfig:: fastapi_aad_auth.config.Config
:noindex:
28 changes: 24 additions & 4 deletions docs/source/development.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ To run tests locally execute:
This will run the test suite for the same Python version as under which ``tox`` is installed.

.. _testing:

Integration tests
~~~~~~~~~~~~~~~~~

Expand All @@ -61,12 +63,17 @@ To run the testapp use tox (or directly with ``python tests/testapp/server.py``:
tox -e testapp
This will run the test suite for the same Python version as under which ``tox`` is installed.
This will run the test app for the same Python version as under which ``tox`` is installed.


Configuring the testApp
Configuring the testapp
#######################

The testapp requires configuring (see :ref:`config-aad-appreg`) for how to configure an appropriate App Registration on Azure.
The app provides a really simple set of tests - the home page has no authentication, but if you have logged in, it will say "hello <email>",
with your logged in email.

The api docs (swagger UI) is authentication limited, as is the simple api endpoint '/hello'.



Expand Down Expand Up @@ -104,8 +111,8 @@ Release

We release through GitHub using an automated process to collate and test the releases.

Contributing
-------------
Developing
------------

Submitting pull requests
~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -133,3 +140,16 @@ All pull requests and merges to ``master`` branch are tested using
Github actions (configured by ``.github/workflows/pipeline.yml`` file. You can find the status and results to the CI runs for your
PR on GitHub's Web UI for the pull request. You can also find links to the CI services' pages for the specific builds in
the form of "Details" links, in case the CI run fails and you wish to view the output.

.. _compatibility-requirements:

Compatibility Requirements
~~~~~~~~~~~~~~~~~~~~~~~~~~

We endeavour to support (and test) on multiple python versions, across the following matrix of os and python versions:

.. literalinclude:: ../../.github/workflows/pipeline.yml
:language: yaml
:start-after: matrix:
:end-before: max-parallel
:dedent: 8
14 changes: 12 additions & 2 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,22 @@ The repository is open source (MIT Licensed) on |github| `Github <https://github


.. toctree::
:caption: Basic Use
:caption: Usage
:maxdepth: 1

Usage <usage>
Configuration Options <config>
Basic Usage <usage>
Advanced Usage <advanced>

.. toctree::
:caption: API
:maxdepth: 4

module/fastapi_aad_auth.auth
module/fastapi_aad_auth.config
module/fastapi_aad_auth.errors
module/fastapi_aad_auth.oauth
module/fastapi_aad_auth.ui

.. toctree::
:caption: Changes and Contributing
Expand Down
7 changes: 7 additions & 0 deletions docs/source/module/fastapi_aad_auth.auth.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
fastapi_aad_auth.auth
*********************


.. automodule:: fastapi_aad_auth.auth
:members:

5 changes: 5 additions & 0 deletions docs/source/module/fastapi_aad_auth.config.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fastapi_aad_auth.config
***********************

.. automodule:: fastapi_aad_auth.config
:members:
5 changes: 5 additions & 0 deletions docs/source/module/fastapi_aad_auth.errors.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fastapi_aad_auth.errors
***********************

.. automodule:: fastapi_aad_auth.errors
:members:
5 changes: 5 additions & 0 deletions docs/source/module/fastapi_aad_auth.oauth._base.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fastapi_aad_auth.oauth._base
****************************

.. automodule:: fastapi_aad_auth.oauth._base
:members:
5 changes: 5 additions & 0 deletions docs/source/module/fastapi_aad_auth.oauth.aad.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fastapi_aad_auth.oauth.aad
**************************

.. automodule:: fastapi_aad_auth.oauth.aad
:members:
10 changes: 10 additions & 0 deletions docs/source/module/fastapi_aad_auth.oauth.authenticators.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
fastapi_aad_auth.oauth.authenticators
*************************************

.. automodule:: fastapi_aad_auth.oauth.authenticators

.. toctree::
:caption: Sub-modules:
:maxdepth: 1

fastapi_aad_auth.oauth.authenticators.session
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fastapi_aad_auth.oauth.authenticators.session
*********************************************

.. automodule:: fastapi_aad_auth.oauth.authenticators.session
:members:
14 changes: 14 additions & 0 deletions docs/source/module/fastapi_aad_auth.oauth.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
fastapi_aad_auth.oauth
**********************

.. automodule:: fastapi_aad_auth.oauth

.. toctree::
:caption: Sub-modules:
:maxdepth: 1

fastapi_aad_auth.oauth._base
fastapi_aad_auth.oauth.aad
fastapi_aad_auth.oauth.authenticators
fastapi_aad_auth.oauth.state
fastapi_aad_auth.oauth.validators
5 changes: 5 additions & 0 deletions docs/source/module/fastapi_aad_auth.oauth.state.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fastapi_aad_auth.oauth.state
****************************

.. automodule:: fastapi_aad_auth.oauth.state
:members:
11 changes: 11 additions & 0 deletions docs/source/module/fastapi_aad_auth.oauth.validators.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fastapi_aad_auth.oauth.validators
*********************************

.. automodule:: fastapi_aad_auth.oauth.validators

.. toctree::
:caption: Sub-modules:
:maxdepth: 1

fastapi_aad_auth.oauth.validators.session
fastapi_aad_auth.oauth.validators.token
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fastapi_aad_auth.oauth.validators.session
*****************************************

.. automodule:: fastapi_aad_auth.oauth.validators.session
:members:
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fastapi_aad_auth.oauth.validators.token
***************************************

.. automodule:: fastapi_aad_auth.oauth.validators.token
:members:
4 changes: 4 additions & 0 deletions docs/source/module/fastapi_aad_auth.ui.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fastapi_aad_auth.ui
*******************

.. automodule:: fastapi_aad_auth.ui
13 changes: 0 additions & 13 deletions docs/source/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -210,16 +210,3 @@ Tools like Postman allow you to configure authentication via oauth - this shows
:alt: Overview of authenticating for postman

An example of how to configure client credentials (using another app registration) for postman - replace the {tenant} and {appid} info, along with the client id and client secret


Accessing User Tokens/View
~~~~~~~~~~~~~~~~~~~~~~~~~~

There are two routes that are automatically added to this, the ``/me`` and ``/me/getToken`` routes. The ``/me`` route provides a summary of the current user, and enables them to get a bearer token from Azure AD.
The ``/me/token`` endpoint provides that same token (for the logged in user) in a JSON object

.. warning::

To get the token, this is primarily an interactive method, as it requires caching the token through the UI session based login approach, so it can fail intermittently depending on if the user has logged in recently.

This can be disabled by setting the ``config.routing.user_path`` to ``None`` or ``''``.
Loading

0 comments on commit 78e2f94

Please sign in to comment.