Source code for falcon_authentication.middleware

# -*- coding: utf-8 -*-

from __future__ import absolute_import
from __future__ import division

import falcon

from falcon_authentication.backends import AuthBackend, BackendAuthenticationFailure


[docs]class FalconAuthMiddleware(object): """ Creates a falcon auth middleware that uses given authentication backend, and some optional configuration to authenticate requests. After initializing the authentication backend globally you can override the backend as well as other configuration for a particular resource by setting the `auth` attribute on it to an instance of this class. The authentication backend must return an authenticated user which is then set as ``request.context['auth']['user']`` to be used further down by resources othewise an ``falcon.HTTPUnauthorized`` exception is raised. Args: backend(:class:`falcon_authentication.backends.AuthBackend`, required): Specifies the auth backend to be used to authenticate requests exempt_routes(list, optional): A list of paths to be excluded while performing authentication. Default is ``None`` exempt_methods(list, optional): A list of paths to be excluded while performing authentication. Default is ``['OPTIONS']`` context_key(str, optional): The key to be used when adding the successful authentication results to the ``req.context`` dictionary. Default is ``'auth'``. on_success(function, optional): A callback function that is called with the results of the ``authenticate()`` call when authentication succeeds. .. code:: python def on_success(req, resp, resource, results): ... on_failure(function, optional): A callback function that is called with the results of the ``authenticate()`` call when authentication fails. This will only be called if the backend was deemed appropriate for the request (ie. a ``falcon.HTTPUnauthorized`` exception was raised and not a ``BackendNotApplicable`` exception). .. code:: python def on_failure(req, resp, resource, exception): ... """ def __init__(self, backend, exempt_routes=None, exempt_methods=None, context_key='auth', on_success=None, on_failure=None): self.backend = backend self.on_success = on_success self.on_failure = on_failure if not isinstance(backend, AuthBackend): raise ValueError( ( 'Invalid authentication backend {0}.' ' Must inherit falcon.auth.backends.AuthBackend' ).format(backend.__class__.__name__) ) self.exempt_routes = exempt_routes or [] self.exempt_methods = exempt_methods or ['OPTIONS'] self.context_key = context_key def _get_auth_settings(self, req, resource): auth_settings = getattr(resource, 'auth', {}) auth_settings['exempt_routes'] = self.exempt_routes if auth_settings.get('auth_disabled'): auth_settings['exempt_routes'].append(req.path) for key in ('exempt_methods', 'backend'): auth_settings[key] = auth_settings.get(key) or getattr(self, key) return auth_settings def process_resource(self, req, resp, resource, *args, **kwargs): auth_setting = self._get_auth_settings(req, resource) if (req.uri_template in auth_setting['exempt_routes'] or req.method in auth_setting['exempt_methods']): return backend = auth_setting['backend'] try: results = backend.authenticate(req, resp, resource, **kwargs) # Use setdefault() here to accommodate MultiAuthBackend that # sets this value to the successful backend type in its list. results.setdefault('backend', backend) req.context[self.context_key] = results if self.on_success: self.on_success(req, resp, resource, results) except BackendAuthenticationFailure as ex: if self.on_failure: # Notify the application about the failure and the backend # that raised it before re-raising it. self.on_failure(req, resp, resource, ex) raise except Exception as ex: if self.on_failure: # As we wish to convey which backend was responsible for # the authentication failure, we wrap any # non-BackendAuthenticationFailure exceptions in one and # provide that to the application. The original exception # will be re-raised. backend_auth_failure = BackendAuthenticationFailure( backend, 'Unexpected authentication error') backend_auth_failure.__cause__ = ex self.on_failure(req, resp, resource, backend_auth_failure) raise