Skip to content
Snippets Groups Projects
middleware.py 6.63 KiB
Newer Older
Colin Murtaugh's avatar
Colin Murtaugh committed
from django.contrib import auth

from django.core.exceptions import ImproperlyConfigured, PermissionDenied

Colin Murtaugh's avatar
Colin Murtaugh committed
from timer import Timer

Colin Murtaugh's avatar
Colin Murtaugh committed
import logging
Colin Murtaugh's avatar
Colin Murtaugh committed
logger = logging.getLogger(__name__)


class LTIAuthMiddleware(object):
    """
    Middleware for authenticating users via an LTI launch URL.

    If the request is an LTI launch request, then this middleware attempts to
    authenticate the username and signature passed in the POST data.
Colin Murtaugh's avatar
Colin Murtaugh committed
    If authentication is successful, the user is automatically logged in to
    persist the user in the session.

    If the request is not an LTI launch request, do nothing.
Colin Murtaugh's avatar
Colin Murtaugh committed
    """

    def process_request(self, request):
        logger.debug('inside process_request %s' % request.path)
Colin Murtaugh's avatar
Colin Murtaugh committed
        # AuthenticationMiddleware is required so that request.user exists.
        if not hasattr(request, 'user'):
            logger.debug('improperly configured: requeset has no user attr')
            raise ImproperlyConfigured(
                "The Django LTI auth middleware requires the"
Colin Murtaugh's avatar
Colin Murtaugh committed
                " authentication middleware to be installed.  Edit your"
                " MIDDLEWARE_CLASSES setting to insert"
                " 'django.contrib.auth.middleware.AuthenticationMiddleware'"
                " before the PINAuthMiddleware class.")

        if request.method == 'POST' and request.POST.get('lti_message_type') == 'basic-lti-launch-request':
Colin Murtaugh's avatar
Colin Murtaugh committed

            logger.debug('received a basic-lti-launch-request - authenticating the user')
Colin Murtaugh's avatar
Colin Murtaugh committed

            # authenticate and log the user in
Colin Murtaugh's avatar
Colin Murtaugh committed
            with Timer() as t:
                user = auth.authenticate(request=request)
            logger.debug('authenticate() took %s s' % t.secs)
Colin Murtaugh's avatar
Colin Murtaugh committed

Colin Murtaugh's avatar
Colin Murtaugh committed
            if user is not None:
Colin Murtaugh's avatar
Colin Murtaugh committed
                # User is valid.  Set request.user and persist user in the session
                # by logging the user in.
Colin Murtaugh's avatar
Colin Murtaugh committed
                logger.debug('user was successfully authenticated; now log them in')
                request.user = user
Colin Murtaugh's avatar
Colin Murtaugh committed
                with Timer() as t:
                    auth.login(request, user)
    
                logger.debug('login() took %s s' % t.secs)

                lti_launch = {
                    'custom_canvas_account_id': request.POST.get('custom_canvas_account_id', None),
                    'lis_outcome_service_url': request.POST.get('lis_outcome_service_url', None),
                    'lti_message_type': request.POST.get('lti_message_type', None),
                    'resource_link_id': request.POST.get('resource_link_id', None),
                    'user_image': request.POST.get('user_image', None),
                    'lis_outcome_service_url': request.POST.get('lis_outcome_service_url', None),
                    'lis_course_offering_sourcedid': request.POST.get('lis_course_offering_sourcedid', None),
                    'selection_directive': request.POST.get('selection_directive', None),
                    'resource_link_title': request.POST.get('resource_link_title', None),
                    'resource_link_description': request.POST.get('resource_link_description', None),
                    'context_id': request.POST.get('context_id', None),
                    'context_type': request.POST.get('context_type', None),
                    'context_title': request.POST.get('context_title', None),
                    'context_label': request.POST.get('context_label', None),
                    'launch_presentation_locale': request.POST.get('launch_presentation_locale', None),
                    'launch_presentation_css_url': request.POST.get('launch_presentation_css_url', None),
                    'launch_presentation_width': request.POST.get('launch_presentation_width', None),
                    'launch_presentation_height': request.POST.get('launch_presentation_height', None),
                    'launch_presentation_return_url': request.POST.get('launch_presentation_return_url', None),
                    'launch_presentation_document_target': request.POST.get('launch_presentation_document_target', None),
                    'tool_consumer_info_product_family_code': request.POST.get('tool_consumer_info_product_family_code', None),
                    'tool_consumer_info_version': request.POST.get('tool_consumer_info_version', None),
                    'tool_consumer_instance_guid': request.POST.get('tool_consumer_instance_guid', None),
                    'tool_consumer_instance_name': request.POST.get('tool_consumer_instance_name', None),
                    'tool_consumer_instance_description': request.POST.get('tool_consumer_instance_description', None),
                    'tool_consumer_instance_url': request.POST.get('tool_consumer_instance_url', None),
                    'tool_consumer_instance_contact_email': request.POST.get('tool_consumer_instance_contact_email', None),
                    'lis_person_name_given': request.POST.get('lis_person_name_given', None),
                    'lis_person_name_family': request.POST.get('lis_person_name_family', None),
                    'lis_person_contact_email_primary': request.POST.get('lis_person_contact_email_primary', None),
                    'lis_person_name_full': request.POST.get('lis_person_name_full', None),
                    'user_id': request.POST.get('user_id', None),
                    'custom_canvas_user_id': request.POST.get('custom_canvas_user_id', None),
                    'custom_canvas_api_domain': request.POST.get('custom_canvas_api_domain', None),
                    'custom_canvas_enrollment_state': request.POST.get('custom_canvas_enrollment_state', None),
                    'custom_canvas_course_id': request.POST.get('custom_canvas_course_id', None),
                    'custom_canvas_user_login_id': request.POST.get('custom_canvas_user_login_id', None),
                    'roles': request.POST.get('roles', '').split(','),
                }

                request.session['LTI_LAUNCH'] = lti_launch

Colin Murtaugh's avatar
Colin Murtaugh committed
            else:
                # User could not be authenticated!
                logger.warning('user could not be authenticated via LTI params; let the request continue in case another auth plugin is configured')
Colin Murtaugh's avatar
Colin Murtaugh committed

    def clean_username(self, username, request):
        """
        Allows the backend to clean the username, if the backend defines a
        clean_username method.
        """
        backend_str = request.session[auth.BACKEND_SESSION_KEY]
        backend = auth.load_backend(backend_str)
        try:
            logger.debug('calling the backend %s clean_username with %s' % (backend, username))
Colin Murtaugh's avatar
Colin Murtaugh committed
            username = backend.clean_username(username)
            logger.debug('cleaned username is %s' % username)
        except AttributeError:  # Backend has no clean_username method.
            pass
        return username