FAQ | This is a LIVE service | Changelog

Refactor code

Created by: rjw57

This repository satisfies the immediate need to group common code together in one place. Now we need to do the work to re-factor it into something re-usable.

An initial proposal:

Related but distinct functionality is grouped together into several top-level modules/applications.

Rationale: a Django project which doesn't use DRF shouldn't have to add it to their INSTALLED_APPS just to use the Lookup functionality in this repo.

Module names should follow the conventions of the projects they extend where defined.

Rationale: We want it to be easy to use the functionality in these modules

Documentation should be high-quality

Rationale: If we want to encourage others to adopt similar solutions, our implementations should be easy to follow.

OAuth2 client

The OAuth2 session functionality should be available from a top-level module. E.g.

# Use default session
from automationoauth2 import session
session.request(...)

# Create new session
from automationoauth2 import AuthenticatedSession
special_session = AuthenticatedSession()
special_session.request(...)

The client should use AUTOMATION_OAUTH2_CLIENT_{ID,SECRET,TOKEN_URL} settings. These should be required by way of custom system checks which are run when automationoauthclient is added to INSTALLED_APPS.

This module can also verify tokens and retrieve the subject:

from automationoauth2 import verify_token, InvalidTokenError

# ... token is obtained from request
try:
    # verify_token caches token verification for a short period of time
    # so it can be called repeatedly
    token_info = verify_token(token)
except InvalidTokenError:
    # InvalidTokenError inherits from ValueError
    # handle error...

The token info should look like:

{
    'subject': 'some:subject:urn',   # as returned by Hydra
    'client': 'some-client-id',  # client id of client which obtained token
    # ... other information as appropriate, including basic lookup info
}

DRF integration

Provides a set of permissions and authentication classes. E.g.

from drf_automationoauth2.authentication import OAuth2TokenAuthentication
from drf_automationoauth2.permissions import HasScopesPermission

class MyViews(ModelViewSet):
    authentication_classes = (OAuth2TokenAuthentication,)
    permission_classes = (HasScopesPermission,)
    required_scopes = ['scope1', 'scope2']

The OAuth2TokenAuthentication authentication class populates request.token. request.user is not populated. Trying to turn tokens into Django users was a mistake motivated by a desire to make use of the existing django.contrib.auth permissions system and permissions admin interface. This just meant we needed a brittle mapping between token subjects, lookup identities and Django users.

Lookup functionality

Provides a client for lookupproxy.

from automationlookup import get_person_for_subject
from automationoauth2 import verify_token

token_info = verify_token(token)
person = get_person_for_subject(token_info['subject'])

# custom authentication

from automationoauth2 import AuthenticatedSession
special_session = AuthenticatedSession(client_id='xxx', client_secret='yyy')
person = get_person_for_subject(token_info['subject'], session=special_session)

The lookupproxy client is authenticated by default using the default session from automationoauth2.

Edited by Dr Abraham Martin