Example #1
0
 def post(self, request, pk):
     '''
     The LTI tool provider uses POST to send a real, OAuth-signed, request for the LTI provider content.
     '''
     logger.debug("Incoming POST request through LTI")
     tool_provider = DjangoToolProvider.from_django_request(request=request)
     validator = LtiRequestValidator(pk)
     if tool_provider.is_valid_request(validator):
         logger.debug("Valid OAuth request through LTI")
         if request.user.is_authenticated:
             logger.debug("LTI consumer is already authenticated")
             return redirect(reverse('lti_submission', args=[pk]))
         else:
             logger.debug(
                 "LTI consumer needs OpenSubmit user, starting auth pipeline"
             )
             # Store data being used by the Django Social Auth Provider for account creation
             data = request.POST.copy()
             data['assignment_pk'] = pk
             request.session[lti.SESSION_VAR] = data
             return redirect(
                 reverse('social:begin', args=['lti']) + "?next=" +
                 reverse('lti_submission', args=[pk]))
     else:
         logger.error("Invalid OAuth request through LTI")
         raise PermissionDenied
Example #2
0
    def _validate_request(self, request):
        """
        Validates an LTI launch request.
        """
        validator = LTIRequestValidator()
        tool_provider = DjangoToolProvider.from_django_request(request=request)

        postparams = request.POST.dict()
        self.logger.debug("request is secure: %s" % request.is_secure())
        for key in postparams:
            self.logger.debug("POST %s: %s" % (key, postparams.get(key)))
        self.logger.debug("request abs url is %s" %
                          request.build_absolute_uri())
        for key in request.META:
            self.logger.debug("META %s: %s" % (key, request.META.get(key)))

        self.logger.debug("about to check the signature")
        # NOTE: before validating the request, temporarily remove the
        # QUERY_STRING to work around an issue with how Canvas signs requests
        # that contain GET parameters. Before Canvas launches the tool, it duplicates the GET
        # parameters as POST parameters, and signs the POST parameters (*not* the GET parameters).
        # However, the oauth2 library that validates the request generates
        # the oauth signature based on the combination of POST+GET parameters together,
        # resulting in a signature mismatch. By removing the QUERY_STRING before
        # validating the request, the library will generate the signature based only on
        # the POST parameters like Canvas.
        #
        # 03feb20 naomi: TODO check if removing query string still needed,
        # since change to pylti/lti which uses oauthlib. It looks like oauthlib
        # correctly disregards query string in
        # oauthlib/oauth1/rfc5849/signature:base_string_uri()
        # -- could not force a query string in unit tests using django.test.Client
        qs = request.META.pop("QUERY_STRING", "")
        self.logger.debug("removed query string temporarily: %s" % qs)
        request_is_valid = tool_provider.is_valid_request(validator)
        request.META["QUERY_STRING"] = qs  # restore the query string
        self.logger.debug("restored query string: %s" %
                          request.META["QUERY_STRING"])

        if not request_is_valid:
            self.logger.error("signature check failed")
            raise PermissionDenied

        self.logger.info("signature verified")

        for required_param in ("resource_link_id", "context_id", "user_id"):
            if required_param not in request.POST:
                self.logger.error(
                    "Required LTI param '%s' was not present in request" %
                    required_param)
                raise LTILaunchError(
                    "missing LTI param {}".format(required_param))

        if ("lis_person_sourcedid" not in request.POST
                and "lis_person_name_full" not in request.POST
                and request.POST["user_id"] != "student"):
            self.logger.error(
                "person identifier (i.e. username) or full name was not present in request"
            )
            raise LTILaunchError("missing LTI param: person identifier")
Example #3
0
def lti_launch(request, qset_id=None):
    """
    Endpoint for all requests to embed LMS content via the LTI protocol.

    An LTI launch is successful if:
    - The launch contains all the required parameters
    - The launch data is correctly signed using a known client key/secret pair
    """
    try:
        tool_provider = DjangoToolProvider.from_django_request(request=request)
        validator = SignatureValidator()
        ok = tool_provider.is_valid_request(validator)
    except (oauth1.OAuth1Error, InvalidLTIRequestError, ValueError) as err:
        ok = False
        log.error('Error happened while LTI request: {}'.format(err.__str__()))
    if not ok:
        raise Http404('LTI request is not valid')

    anononcement_page = render(request,
                               template_name="askup_lti/announcement.html",
                               context={
                                   'title': 'announcement',
                                   'message': 'coming soon!',
                                   'tip': 'this subject is about to open.',
                               })

    qset = Qset.objects.filter(id=qset_id).first()

    if not qset_id or not qset:
        return anononcement_page

    return student_lti_flow(request, qset)
Example #4
0
def test_lti_validation_default_key_unknown_consumer_fail():
    target_path = "/some_path"
    launch_url = "http://testserver{}".format(target_path)
    resource_link_id = "some_string_to_be_the_fake_resource_link_id"
    consumer = ToolConsumer(
        consumer_key="unknown_consumer_key",
        consumer_secret=settings.LTI_SECRET,
        launch_url=launch_url,
        params={
            "lti_message_type": "basic-lti-launch-request",
            "lti_version": "LTI-1p0",
            "resource_link_id": resource_link_id,
            "lis_person_sourcedid": "instructor_1",
            "lis_outcome_service_url": "fake_url",
            "user_id": "instructor_1-anon",
            "roles": ["Instructor", "Administrator"],
            "context_id": "fake_course",
        },
    )
    params = consumer.generate_launch_data()
    for key in params:
        print("****** LTI[{}]: {}".format(key, params[key]))

    factory = RequestFactory()
    request = factory.post(target_path, data=params)

    validator = LTIRequestValidator()
    tool_provider = DjangoToolProvider.from_django_request(request=request)
    request_is_valid = tool_provider.is_valid_request(validator)

    assert not request_is_valid
Example #5
0
def test_lti_validation_from_ltidict_ok():
    target_path = reverse("hx_lti_initializer:launch_lti")
    launch_url = "http://testserver{}".format(target_path)
    resource_link_id = "some_string_to_be_the_fake_resource_link_id"
    consumer = ToolConsumer(
        consumer_key=settings.CONSUMER_KEY,
        consumer_secret=settings.TEST_COURSE_LTI_SECRET,
        launch_url=launch_url,
        params={
            "lti_message_type": "basic-lti-launch-request",
            "lti_version": "LTI-1p0",
            "resource_link_id": resource_link_id,
            "lis_person_sourcedid": "instructor_1",
            "lis_outcome_service_url": "fake_url",
            "user_id": "instructor_1-anon",
            "roles": ["Instructor", "Administrator"],
            "context_id": settings.TEST_COURSE,
        },
    )
    params = consumer.generate_launch_data()
    for key in params:
        print("****** LTI[{}]: {}".format(key, params[key]))

    factory = RequestFactory()
    request = factory.post(target_path, data=params)

    validator = LTIRequestValidator()
    tool_provider = DjangoToolProvider.from_django_request(request=request)
    request_is_valid = tool_provider.is_valid_request(validator)

    assert request_is_valid
Example #6
0
    def _decorator(request, *args, **kwargs):
        # is this an lti launch request?
        # https://www.imsglobal.org/recipe-making-lti-1-tool-providers
        if request.method == "POST":
            is_basic_lti_launch = (request.POST.get(
                "lti_message_type", "") == "basic-lti-launch-request")
            has_lti_version = request.POST.get("lti_version", "") == "LTI-1p0"
            oauth_consumer_key = request.POST.get("oauth_consumer_key", None)
            resource_link_id = request.POST.get("resource_link_id", None)
        else:
            return HttpResponseBadRequest()

        if not (is_basic_lti_launch and has_lti_version and oauth_consumer_key
                and resource_link_id):
            return HttpResponseBadRequest()

        # lti request authentication
        validator = LTIRequestValidator()
        tool_provider = DjangoToolProvider.from_django_request(
            secret=validator.get_client_secret(oauth_consumer_key, request),
            request=request,
        )

        valid_lti_request = tool_provider.is_valid_request(validator)

        if valid_lti_request:
            response = view_func(request, *args, **kwargs)
            return response
        else:
            return HttpResponseForbidden()
Example #7
0
def lti_launch(request, slug):
    app = get_object_or_404(LTIApp, slug=slug)
    tool_provider = DjangoToolProvider.from_django_request(request=request)
    validator = LTIRequestValidator()
    ok = tool_provider.is_valid_request(validator)
    if not ok:
        return HttpResponseForbidden(
            'The launch request is considered invalid')

    client_key = tool_provider.consumer_key  # request would not be ok if this is not set
    try:
        tenant = app.ltitenant_set.get(client_key=client_key)
    except LTITenant.DoesNotExist:
        return HttpResponseForbidden(
            '{} does not have access to app {}'.format(client_key, app))

    # First thing is to create and login a user, because this influences the session
    if app.privacy_level != LTIPrivacyLevels.ANONYMOUS:
        user = authenticate(
            request,
            remote_user=request.POST.get("lis_person_contact_email_primary"))
        if user is not None:
            login(request, user)

    # After we have a user we're gonna set its session based on tenant settings
    # This authorizes a user to use tenant LMS API's
    tenant.start_session(request, request.POST.dict())

    # Lookup the view and return its response
    # Redirect impossible because we need to set cookies for sessions
    url = reverse(app.view)
    view = resolve(url)
    return view.func(request)
 def test_secret_not_required(self):
     from lti.contrib.django import DjangoToolProvider
     mock_req = Mock()
     mock_req.POST = {'oauth_consumer_key': 'foo'}
     mock_req.META = {'CONTENT_TYPE': 'bar'}
     mock_req.build_absolute_uri.return_value = 'http://example.edu/foo/bar'
     tp = DjangoToolProvider.from_django_request(request=mock_req)
     self.assertEqual(tp.consumer_key, 'foo')
     self.assertEqual(tp.launch_headers['CONTENT_TYPE'], 'bar')
     self.assertEqual(tp.launch_url, 'http://example.edu/foo/bar')
Example #9
0
 def test_secret_not_required(self):
     from lti.contrib.django import DjangoToolProvider
     mock_req = Mock()
     mock_req.POST = {'oauth_consumer_key': 'foo'}
     mock_req.META = {'CONTENT_TYPE': 'bar'}
     mock_req.build_absolute_uri.return_value = 'http://example.edu/foo/bar'
     tp = DjangoToolProvider.from_django_request(request=mock_req)
     self.assertEqual(tp.consumer_key, 'foo')
     self.assertEqual(tp.launch_headers['CONTENT_TYPE'], 'bar')
     self.assertEqual(tp.launch_url, 'http://example.edu/foo/bar')
Example #10
0
    def _get_tool_provider(self):
        try:
            lti_secret = settings.LTI_SECRET_DICT[self.request.LTI.get(
                "hx_context_id")]
        except KeyError:
            lti_secret = settings.LTI_SECRET

        if "launch_params" in self.request.LTI:
            params = self.request.LTI["launch_params"]

            # the middleware includes an LTI dict with all lti params for
            # lti_grade_passback() -- an lti request that is not a lti-launch.
            # py-lti only understands lti params that come directly in the POST
            mutable_post = self.request.POST.copy()
            mutable_post.update(params)
            self.request.POST = mutable_post

            return DjangoToolProvider.from_django_request(lti_secret,
                                                          request=self.request)
        return DjangoToolProvider.from_django_request(lti_secret,
                                                      request=self.request)
Example #11
0
def get_tool_provider_for_lti(request):
    """
    Return tool provider for the given request.

    In case of invalid lti request return None.
    """
    try:
        tool_provider = DjangoToolProvider.from_django_request(request=request)
        validator = SignatureValidator()
        if tool_provider.is_valid_request(validator):
            return tool_provider
    except (oauth1.OAuth1Error, InvalidLTIRequestError, ValueError) as err:
        log.error('Error happened while LTI request: {}'.format(err.__str__()))
    return None
Example #12
0
    def test_learner_flow_different_user_creation(self):
        mock_request = RequestFactory().post(
            '',
            data={
                'oauth_nonce': 'oauth_nonce',
                'oauth_consumer_key': self.lti_provider.consumer_key,
                'roles': 'Learner',
                'user_id': 'user_id',
                'context_id': 'some+course+id'
            })
        middleware = SessionMiddleware()
        middleware.process_request(mock_request)
        mock_request.session.save()

        tool_provider = DjangoToolProvider.from_django_request(
            request=mock_request)

        count_of_the_sequence = Sequence.objects.all().count()
        count_of_lti_users = LtiUser.objects.all().count()

        # learner_flow is called 2 times (here and below) to ensure that implement logic works correctly

        learner_flow(mock_request, self.lti_provider, tool_provider,
                     self.collection1.slug, self.test_cg.slug)
        learner_flow(mock_request, self.lti_provider, tool_provider,
                     self.collection1.slug, self.test_cg.slug)
        self.assertEqual(Sequence.objects.all().count(),
                         count_of_the_sequence + 1)

        count_of_the_sequence += 1
        learner_flow(mock_request, self.lti_provider, tool_provider,
                     self.collection1.slug, self.test_cg.slug, 'marker')
        learner_flow(mock_request, self.lti_provider, tool_provider,
                     self.collection1.slug, self.test_cg.slug, 'marker')
        self.assertEqual(Sequence.objects.all().count(),
                         count_of_the_sequence + 1)

        count_of_the_sequence += 1
        learner_flow(mock_request, self.lti_provider, tool_provider,
                     self.collection1.slug, self.test_cg.slug, 'marker1')
        learner_flow(mock_request, self.lti_provider, tool_provider,
                     self.collection1.slug, self.test_cg.slug, 'marker2')
        self.assertEqual(Sequence.objects.all().count(),
                         count_of_the_sequence + 2)

        # Ensure that only one LTI user was created.
        self.assertEqual(LtiUser.objects.all().count(), count_of_lti_users + 1)
Example #13
0
    def post(self, request):
        tool_provider = DjangoToolProvider.from_django_request(request=request)
        validator = utils.OutpostRequestValidator()
        ok = tool_provider.is_valid_request(validator)
        if not ok:
            return HttpResponseForbidden()

        try:
            consumer = Consumer.objects.get(key=tool_provider.consumer_key,
                                            enabled=True)
        except Consumer.DoesNotExist:
            return HttpResponseForbidden()

        params = tool_provider.to_params()
        username = params.get("ext_user_username")
        user = get_user_model().objects.get(username=username)
        login(request,
              user,
              backend="django.contrib.auth.backends.ModelBackend")
        # import pudb; pu.db
        language = params.get("launch_presentation_locale",
                              settings.LANGUAGE_CODE)
        with translation.override(language):
            try:
                resource = Resource.objects.get(
                    consumer=consumer, resource=params.get("resource_link_id"))
                return resource.render(request, consumer, user, tool_provider)
            except Resource.DoesNotExist:
                roles = params.get("roles")
                grs = GroupRole.objects.filter(role__in=roles)
                groups = user.groups.all()
                for gr in grs:
                    if gr.group not in groups:
                        user.groups.add(gr.group)
                if not user.has_perm("lti.add_resource"):
                    return HttpResponseForbidden()
                return TemplateResponse(
                    request,
                    "lti/index.html",
                    {
                        "consumer": consumer,
                        "user": user,
                        "tool_provider": tool_provider,
                        "resource_classes": Resource.__subclasses__(),
                    },
                )
Example #14
0
def validate_request(request):
    """
    Function to validate that the request is a valid LTI Launch Request.
    """
    if request is None:
        raise ValueError("Request can't be none!")
    params = request.POST.copy()
    url = request.build_absolute_uri()
    consumers = settings.LTI_OAUTH_CREDENTIALS
    valid_lti = verify_request_common(consumers, url, request.method,
                                      request.META, params)
    # TODO: If possible, then implement the oauth verification
    # TODO: remove DjangoToolProvider thing from here!
    '''
    print("validity of valid_lti = ", valid_lti)
    validator = ProxyValidator(LTIValidator())
    endpoint = SignatureOnlyEndpoint(validator)
    # oauth validation for nonce, timestamp etc...
    headers = dict([(k, request.META[k])
                    for k in request.META if
                    k.upper().startswith('HTTP_') or
                    k.upper().startswith('CONTENT_')])
    print(request.method)
    valid_req, request = endpoint.validate_request(
        url,
        request.method,
        to_params(params),
        headers
    )
    print("validity of valid_req = ", request)
    '''
    tool_provider = DjangoToolProvider.from_django_request(request=request)

    # the tool provider uses the 'oauthlib' library which requires an instance
    # of a validator class when doing the oauth request signature checking.
    # see https://oauthlib.readthedocs.org/en/latest/oauth1/validator.html for
    # info on how to create one
    validator = LTIValidator()
    # validate the oauth request signature
    ok = tool_provider.is_valid_request(validator)

    return valid_lti and ok
Example #15
0
 def post(self, request, pk):
     '''
     The LTI tool provider uses POST to send a real, OAuth-signed, request for the LTI provider content.
     '''
     logger.debug("Incoming POST request through LTI")
     from lti.contrib.django import DjangoToolProvider
     tool_provider = DjangoToolProvider.from_django_request(request=request)
     if not tool_provider.launch_url.startswith(settings.HOST):
         # When OpenSubmit runs behind an SSL-terminating proxy,
         # we run into the issue that the launch URL determined from the tool
         # provider is not the original one. Since OAuth requests are signed
         # with the called URL, this breaks the OAuth signature check
         # coming afterwards.
         #
         # The solution is to fix the URL manually in this special case.
         adjusted_url = settings.HOST + '/' + \
             tool_provider.launch_url.split('/', 3)[3]
         logger.info(
             "Changing LTI launch URL from {0} to {1}, based on OpenSubmit configuration, before OAuth check."
             .format(tool_provider.launch_url, adjusted_url))
         tool_provider.launch_url = adjusted_url
     validator = LtiRequestValidator(pk)
     if tool_provider.is_valid_request(validator):
         logger.debug("Valid OAuth request through LTI")
         if request.user.is_authenticated:
             logger.debug("LTI consumer is already authenticated")
             return redirect(reverse('lti_submission', args=[pk]))
         else:
             logger.debug(
                 "LTI consumer needs OpenSubmit user, starting auth pipeline"
             )
             # Store data being used by the Django Social Auth Provider for account creation
             data = request.POST.copy()
             data['assignment_pk'] = pk
             request.session[lti.SESSION_VAR] = data
             return redirect(
                 reverse('social:begin', args=['lti']) + "?next=" +
                 reverse('lti_submission', args=[pk]))
     else:
         logger.error("Invalid OAuth request through LTI")
         raise PermissionDenied
Example #16
0
def login(request):
    context = {}
    student = ''
    teacher = ''

    request.session['user'] = False
    request.session['student'] = False
    user = request.user
    user_type = user.user_type
    #context['create_user_form'] = TempUserForm()
    if request.method == 'POST':
        request_key = request.POST.get('oauth_consumer_key', None)
        timestamp = request.POST.get('oauth_timestamp', None)
        nonce = request.POST.get('oauth_nonce', None)

        tool_provider = DjangoToolProvider.from_django_request(request=request)
        validator = SignatureValidator(tool_provider)
        check_key = validator.check_client_key(request_key)
        if not check_key:
            logger.error("Invalid request: key check failed.")
            raise PermissionDenied
        check_req = validator.verify(request)
        if not check_req:
            logger.error("Invalid request: signature check failed.")
            raise PermissionDenied
        check_timestamp = validator.validate_timestamp_and_nonce(request_key, timestamp, nonce, request)
        if not check_timestamp:
            logger.error("Invalid request: timestamp check failed")

        code = request.POST.get('code', None)
        if user_type == 't':
            request.session['user'] = user.id
            request.session.set_expiry(3000)
            return redirect('base:dashboard')
        else:
            request.session['student'] = user.id
            request.session.set_expiry(600)
            return render(request, 'base/login_form.html', context)
    else:
        return render(request, 'base/login_form.html', context)
Example #17
0
def validate_lti_request(request):
    """
    Check if LTI launch request is valid, and raise an exception if request is not valid
    An LTI launch is valid if:
    - The launch contains all the required parameters
    - The launch data is correctly signed using a known client key/secret pair
    :param request:
    :return: none
    """
    try:
        tool_provider = DjangoToolProvider.from_django_request(request=request)
        # validate based on originating protocol if using reverse proxy
        if request.META.get('HTTP_X_FORWARDED_PROTO') == 'https':
            tool_provider.launch_url = tool_provider.launch_url.replace(
                'http:', 'https:', 1)
        validator = SignatureValidator()
        is_valid_lti_request = tool_provider.is_valid_request(validator)
    except (oauth1.OAuth1Error, InvalidLTIRequestError, ValueError) as err:
        is_valid_lti_request = False
        log.error('Error happened while LTI request: {}'.format(err.__str__()))
    if not is_valid_lti_request:
        raise Http404('LTI request is not valid')
Example #18
0
def login(request):
    '''View to check the provided LTI credentials.

    Getting in with a faked LTI consumer basically demands a
    staff email adress and a valid LTI key / secret pair.
    Which makes the latter really security sensitive.
    '''
    post_params = request.POST
    tool_provider = DjangoToolProvider.from_django_request(request=request)
    validator = LtiRequestValidator()
    if tool_provider.is_valid_request(validator):
        data = {}
        data['ltikey'] = post_params.get('oauth_consumer_key')
        # None of them is mandatory
        data['id'] = post_params.get('user_id', None)
        data['username'] = post_params.get('custom_username', None)
        data['last_name'] = post_params.get('lis_person_name_family', None)
        data['email'] = post_params.get('lis_person_contact_email_primary', None)
        data['first_name'] = post_params.get('lis_person_name_given', None)
        request.session[passthrough.SESSION_VAR] = data # this enables the login
        return redirect(reverse('social:begin', args=['lti']))
    else:
        raise PermissionDenied
Example #19
0
    def dispatch(self, request, *args, **kwargs):
        # flow for initial LTI launch
        if request.method == 'POST' and request.POST.get(
                'lti_message_type') == 'basic-lti-launch-request':
            # path to redirect to as GET request
            redirect_path = request.path
            if not request.session.session_key:
                request.session.create()
                log.debug(
                    "LTI Launch: Session key storage in cookie failed; created new session"
                )
                # append session id to end of redirect path
                redirect_path = "{}?{}".format(
                    redirect_path,
                    urlencode({'session': request.session.session_key}))

            # store lti launch params in session before redirecting
            tool_provider = DjangoToolProvider.from_django_request(
                request=request)
            validate_lti_request(tool_provider)
            initialize_lti_session(request, tool_provider)

            # redirect to same view as get instead of post
            return redirect(redirect_path)

        # flow for all other LTI session activity
        else:
            # ensure session is set properly
            set_session(request)
            is_lti_session = check_if_lti_session(request)
            if not is_lti_session:
                log.error(
                    'LTI session is not found, Request cannot be processed')
                raise PermissionDenied(
                    "Content is available only through LTI protocol.")

            return super(LtiMixin, self).dispatch(request, *args, **kwargs)
Example #20
0
def is_valid_request(request: HttpRequest) -> bool:
    """Check whether the request is valid and is accepted by oauth2.

    Raises:
        - api.exceptions.BadRequestException if the request is invalid.
        - django.core.exceptions.PermissionDenied if signature check failed."""
    parameters = parse_parameters(request.POST)
    
    if parameters['lti_message_type'] != 'basic-lti-launch-request':
        raise BadRequestException("LTI request is invalid, parameter 'lti_message_type' "
                                  "must be equal to 'basic-lti-launch-request'")
    
    try:
        tool_provider = DjangoToolProvider.from_django_request(request=request)
        request_is_valid = tool_provider.is_valid_request(RequestValidator())
    except oauth2.Error as e:  # pragma: no cover
        logger.warning("Oauth authentication failed : %s" % str(e))
        request_is_valid = False
    
    if not request_is_valid:
        logger.debug("LTI Authentification aborted: signature check failed with parameters : %s",
                     parameters)
        raise PermissionDenied("Invalid request: signature check failed.")
    return True
Example #21
0
    def authenticate(self, request):

        logger.info("about to begin authentication process")

        request_key = request.POST.get('oauth_consumer_key', None)

        if request_key is None:
            logger.error(
                "Request doesn't contain an oauth_consumer_key; can't continue."
            )
            return None

        if not settings.LTI_OAUTH_CREDENTIALS:
            logger.error("Missing LTI_OAUTH_CREDENTIALS in settings")
            raise PermissionDenied

        secret = settings.LTI_OAUTH_CREDENTIALS.get(request_key)

        if secret is None:
            logger.error("Could not get a secret for key %s" % request_key)
            raise PermissionDenied

        logger.debug('using key/secret %s/%s' % (request_key, secret))
        tool_provider = DjangoToolProvider.from_django_request(secret=secret,
                                                               request=request)

        postparams = request.POST.dict()

        logger.debug('request is secure: %s' % request.is_secure())
        for key in postparams:
            logger.debug('POST %s: %s' % (key, postparams.get(key)))

        logger.debug('request abs url is %s' % request.build_absolute_uri())

        for key in request.META:
            logger.debug('META %s: %s' % (key, request.META.get(key)))

        logger.info("about to check the signature")

        try:
            validator = LTIRequestValidator()
            request_is_valid = tool_provider.is_valid_request(validator)
        except:
            logger.exception('error attempting to validate LTI launch %s',
                             postparams)
            request_is_valid = False

        if not request_is_valid:
            logger.error("Invalid request: signature check failed.")
            #raise PermissionDenied

        logger.info("done checking the signature")

        logger.info("about to check the timestamp: %d" %
                    int(tool_provider.oauth_timestamp))
        if time() - int(tool_provider.oauth_timestamp) > 60 * 60:
            logger.error("OAuth timestamp is too old.")
            #raise PermissionDenied
        else:
            logger.info("timestamp looks good")

        logger.info("done checking the timestamp")

        # if we got this far, the user is good

        user = None

        # Retrieve username from LTI parameter or default to an overridable function return value
        username = tool_provider.lis_person_sourcedid or self.get_default_username(
            tool_provider, prefix=self.unknown_user_prefix)
        username = self.clean_username(username)  # Clean it

        email = tool_provider.lis_person_contact_email_primary
        first_name = tool_provider.lis_person_name_given
        last_name = tool_provider.lis_person_name_family

        logger.info("We have a valid username: %s" % username)

        UserModel = get_user_model()

        # Note that this could be accomplished in one try-except clause, but
        # instead we use get_or_create when creating unknown users since it has
        # built-in safeguards for multiple threads.
        if self.create_unknown_user:
            user, created = UserModel.objects.get_or_create(
                **{
                    UserModel.USERNAME_FIELD: username,
                })

            if created:
                logger.debug('authenticate created a new user for %s' %
                             username)
            else:
                logger.debug('authenticate found an existing user for %s' %
                             username)

        else:
            logger.debug(
                'automatic new user creation is turned OFF! just try to find and existing record'
            )
            try:
                user = UserModel.objects.get_by_natural_key(username)
            except UserModel.DoesNotExist:
                logger.debug('authenticate could not find user %s' % username)
                # should return some kind of error here?
                pass

        # update the user
        if email:
            user.email = email
        if first_name:
            user.first_name = first_name
        if last_name:
            user.last_name = last_name
        user.save()
        logger.debug("updated the user record in the database")

        return user
Example #22
0
 def test_request_required(self):
     from lti.contrib.django import DjangoToolProvider
     with self.assertRaises(ValueError):
         DjangoToolProvider.from_django_request()
Example #23
0
 def test_request_required(self):
     from lti.contrib.django import DjangoToolProvider
     with self.assertRaises(ValueError):
         DjangoToolProvider.from_django_request()