예제 #1
0
    def get_input_fields(self):
        # LTI provides a list of default parameters that might be passed as
        # part of the POST data. These parameters should not be prefixed.
        # Likewise, The creator of an LTI link can add custom key/value parameters
        # to a launch which are to be included with the launch of the LTI link.
        # In this case, we will automatically add `custom_` prefix before this parameters.
        # See http://www.imsglobal.org/LTI/v1p1p1/ltiIMGv1p1p1.html#_Toc316828520
        PARAMETERS = [
            "lti_message_type",
            "lti_version",
            "resource_link_title",
            "resource_link_description",
            "user_image",
            "lis_person_name_given",
            "lis_person_name_family",
            "lis_person_name_full",
            "lis_person_contact_email_primary",
            "lis_person_sourcedid",
            "role_scope_mentor",
            "context_type",
            "context_title",
            "context_label",
            "launch_presentation_locale",
            "launch_presentation_document_target",
            "launch_presentation_css_url",
            "launch_presentation_width",
            "launch_presentation_height",
            "launch_presentation_return_url",
            "tool_consumer_info_product_family_code",
            "tool_consumer_info_version",
            "tool_consumer_instance_guid",
            "tool_consumer_instance_name",
            "tool_consumer_instance_description",
            "tool_consumer_instance_url",
            "tool_consumer_instance_contact_email",
        ]

        client_key, client_secret = self.get_client_key_secret()

        # parsing custom parameters to dict
        custom_parameters = {}

        for custom_parameter in self.custom_parameters:
            try:
                param_name, param_value = [
                    p.strip() for p in custom_parameter.split('=', 1)
                ]
            except ValueError:
                _ = self.runtime.service(self, "i18n").ugettext
                msg = _(
                    'Could not parse custom parameter: {custom_parameter}. Should be "x=y" string.'
                ).format(custom_parameter="{0!r}".format(custom_parameter))
                raise LTIError(msg)

            # LTI specs: 'custom_' should be prepended before each custom parameter, as pointed in link above.
            if param_name not in PARAMETERS:
                param_name = 'custom_' + param_name

            custom_parameters[unicode(param_name)] = unicode(param_value)

        return self.oauth_params(
            custom_parameters,
            client_key,
            client_secret,
        )
예제 #2
0
    def verify_oauth_body_sign(self, request, content_type='application/x-www-form-urlencoded'):
        """
        Verify grade request from LTI provider using OAuth body signing.

        Uses http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html::

            This specification extends the OAuth signature to include integrity checks on HTTP request bodies
            with content types other than application/x-www-form-urlencoded.

        Arguments:
            request: DjangoWebobRequest.

        Raises:
            LTIError if request is incorrect.
        """

        client_key, client_secret = self.get_client_key_secret()
        headers = {
            'Authorization': six.text_type(request.headers.get('Authorization')),
            'Content-Type': content_type,
        }

        sha1 = hashlib.sha1()
        sha1.update(request.body)
        oauth_body_hash = base64.b64encode(sha1.digest()).decode('utf-8')
        oauth_params = signature.collect_parameters(headers=headers, exclude_oauth_signature=False)
        oauth_headers = dict(oauth_params)
        oauth_signature = oauth_headers.pop('oauth_signature')
        mock_request_lti_1 = mock.Mock(
            uri=six.text_type(six.moves.urllib.parse.unquote(self.get_outcome_service_url())),
            http_method=six.text_type(request.method),
            params=list(oauth_headers.items()),
            signature=oauth_signature
        )
        mock_request_lti_2 = mock.Mock(
            uri=six.text_type(six.moves.urllib.parse.unquote(request.url)),
            http_method=six.text_type(request.method),
            params=list(oauth_headers.items()),
            signature=oauth_signature
        )
        if oauth_body_hash != oauth_headers.get('oauth_body_hash'):
            log.error(
                "OAuth body hash verification failed, provided: {}, "
                "calculated: {}, for url: {}, body is: {}".format(
                    oauth_headers.get('oauth_body_hash'),
                    oauth_body_hash,
                    self.get_outcome_service_url(),
                    request.body
                )
            )
            raise LTIError("OAuth body hash verification is failed.")

        if (not signature.verify_hmac_sha1(mock_request_lti_1, client_secret) and not
                signature.verify_hmac_sha1(mock_request_lti_2, client_secret)):
            log.error("OAuth signature verification failed, for "
                      "headers:{} url:{} method:{}".format(
                          oauth_headers,
                          self.get_outcome_service_url(),
                          six.text_type(request.method)
                      ))
            raise LTIError("OAuth signature verification has failed.")