Ejemplo n.º 1
0
    def test_models_lti_consumer_auth_parameters_edit_mode_predefined(
        self, _mock_rl, _mock_ts, _mock_nonce
    ):
        """
        Verify that instructor LTI authentication parameters are correctly built with edit mode
        """
        instance = LTIConsumerFactory(
            lti_provider_id="lti_provider_test",
            url="http://localhost:8060/lti/videos/3cd0bcc4-0",
        )
        expected_content_parameters = {
            "lti_message_type": "basic-lti-launch-request",
            "lti_version": "LTI-1p0",
            "resource_link_id": "1234",
            "context_id": "example.com",
            "user_id": "richie",
            "lis_person_contact_email_primary": "",
            "roles": "instructor",
            "oauth_consumer_key": "TestOauthConsumerKey",
            "oauth_nonce": "59474787080480293391616018589",
            "oauth_signature": "Y3d9qVSe+W7kA5E9+7JB/NeF2OA=",
            "oauth_signature_method": "HMAC-SHA1",
            "oauth_timestamp": "1616018589",
            "oauth_version": "1.0",
        }

        self.assertDictEqual(
            expected_content_parameters, instance.get_content_parameters(edit=True)
        )
Ejemplo n.º 2
0
    def test_models_lti_consumer_auth_parameters_no_edit_mode_predefined(
        self, _mock_rl, _mock_ts, _mock_nonce
    ):
        """
        Verify that student LTI authentication parameters are correctly built without
        edit mode when credentials come from the settings.
        """
        instance = LTIConsumerFactory(
            lti_provider_id="lti_provider_test",
            url="http://localhost:8060/lti/videos/3cd0bcc4-0",
            # Add credentials on the model to check that they have no influence
            oauth_consumer_key="IgnoredOauthConsumerKey",
            shared_secret="IgnoredSharedSecret",
        )
        expected_content_parameters = {
            "lti_message_type": "basic-lti-launch-request",
            "lti_version": "LTI-1p0",
            "resource_link_id": "1234",
            "context_id": "example.com",
            "user_id": "richie",
            "lis_person_contact_email_primary": "",
            "roles": "student",
            "oauth_consumer_key": "TestOauthConsumerKey",
            "oauth_nonce": "59474787080480293391616018589",
            "oauth_signature": "PkxLai53gItjVvgbccU7AW4HwuY=",
            "oauth_signature_method": "HMAC-SHA1",
            "oauth_timestamp": "1616018589",
            "oauth_version": "1.0",
        }

        self.assertDictEqual(
            expected_content_parameters, instance.get_content_parameters()
        )
Ejemplo n.º 3
0
    def test_models_lti_consumer_auth_parameters_edit_mode_manual(
        self, _mock_rl, _mock_ts, _mock_nonce
    ):
        """
        Verify that instructor LTI authentication parameters are correctly built with
        edit mode when credentials are setup manually.
        """
        instance = LTIConsumerFactory(
            lti_provider_id=None,
            url="http://localhost:8060/lti/videos/3cd0bcc4-0",
            oauth_consumer_key="ManualTestOauthConsumerKey",
            shared_secret="ManualTestSharedSecret",
        )
        expected_content_parameters = {
            "lti_message_type": "basic-lti-launch-request",
            "lti_version": "LTI-1p0",
            "resource_link_id": "1234",
            "context_id": "example.com",
            "user_id": "richie",
            "lis_person_contact_email_primary": "",
            "roles": "instructor",
            "oauth_consumer_key": "ManualTestOauthConsumerKey",
            "oauth_nonce": "59474787080480293391616018589",
            "oauth_signature": "CCnCQtLjPlb+Yr2C0FjYmoVO6Gk=",
            "oauth_signature_method": "HMAC-SHA1",
            "oauth_timestamp": "1616018589",
            "oauth_version": "1.0",
        }

        self.assertDictEqual(
            expected_content_parameters, instance.get_content_parameters(edit=True)
        )
Ejemplo n.º 4
0
    def test_lti_consumer_models_inline_ratio_max(self):
        """The "inline_ratio" field should not accept values bigger than 10"""
        instance = LTIConsumerFactory(inline_ratio=10.01)

        with self.assertRaises(ValidationError) as context:
            instance.full_clean()

        self.assertEqual(
            str(context.exception),
            "{'inline_ratio': ['Ensure this value is less than or equal to 10.']}",
        )
Ejemplo n.º 5
0
    def test_lti_consumer_models_inline_ratio_min(self):
        """The "inline_ratio" field should not accept values smaller than 0.1"""
        instance = LTIConsumerFactory(inline_ratio=0.09)

        with self.assertRaises(ValidationError) as context:
            instance.full_clean()

        self.assertEqual(
            str(context.exception),
            "{'inline_ratio': ['Ensure this value is greater than or equal to 0.1.']}",
        )
Ejemplo n.º 6
0
 def test_lti_consumer_factories_create_with_lti_provider(self):
     """
     The url field should be computed by the model's "save" method if an
     LTI provider is defined.
     """
     lti_consumer = LTIConsumerFactory()
     self.assertIn(lti_consumer.url, "http://localhost:8060/lti/videos/")
Ejemplo n.º 7
0
    def test_lti_consumer_api_get_context(self, mock_params):
        """
        Instianciating this plugin and make a request to its API endpoint
        to get context
        """
        placeholder = Placeholder.objects.create(slot="test")

        lti_consumer = LTIConsumerFactory()
        model_instance = add_plugin(
            placeholder,
            LTIConsumerPlugin,
            "en",
            url=lti_consumer.url,
            lti_provider_id=lti_consumer.lti_provider_id,
        )

        response = self.client.get(
            f"/api/v1.0/plugins/lti-consumer/{model_instance.pk}/context/")
        content = json.loads(response.content)

        self.assertEqual(response.status_code, 200)
        self.assertIn(content["url"], lti_consumer.url)
        self.assertEqual(content["automatic_resizing"],
                         lti_consumer.automatic_resizing)
        self.assertEqual(content["content_parameters"], "test_content")
        mock_params.assert_called_once()
Ejemplo n.º 8
0
    def test_cms_plugins_lti_consumer_context_and_html(self):
        """
        Instanciating this plugin with an instance should populate the context
        and render in the template.
        """
        placeholder = Placeholder.objects.create(slot="test")

        lti_consumer = LTIConsumerFactory()

        model_instance = add_plugin(
            placeholder,
            LTIConsumerPlugin,
            "en",
            url=lti_consumer.url,
            lti_provider_id=lti_consumer.lti_provider_id,
        )
        plugin_instance = model_instance.get_plugin_class_instance()
        context = plugin_instance.render({}, model_instance, None)

        # Check if instance is in context
        self.assertEqual(model_instance, context["instance"])

        # Get generated html for LTI consumer url
        renderer = ContentRenderer(request=RequestFactory())
        html = renderer.render_plugin(model_instance, {})

        # Check rendered url is correct after save and sanitize
        self.assertIn(lti_consumer.url, html)
        self.assertIn("student", html)
Ejemplo n.º 9
0
    def test_lti_consumer_api_get_context(self, mock_params):
        """
        Instantiating this plugin and make a request to its API endpoint
        to get context
        """
        page = create_i18n_page("A page")
        placeholder = page.placeholders.get(slot="maincontent")

        lti_consumer = LTIConsumerFactory()
        model_instance = add_plugin(
            placeholder,
            LTIConsumerPlugin,
            "en",
            url=lti_consumer.url,
            lti_provider_id=lti_consumer.lti_provider_id,
        )

        user_id = str(uuid.uuid4())
        response = self.client.get(
            f"/api/v1.0/plugins/lti-consumer/{model_instance.pk}/context/",
            {"user_id": user_id},
        )
        self.assertEqual(response.status_code, 200)
        content = json.loads(response.content)

        self.assertIn(content["url"], lti_consumer.url)
        self.assertTrue(content["is_automatic_resizing"])
        self.assertEqual(content["content_parameters"], "test_content")
        mock_params.assert_called_once_with(user_infos={"user_id": user_id},
                                            edit=False)
Ejemplo n.º 10
0
    def test_lti_consumer_view_set_get_context_method_is_cached(
            self, mock_params):
        """
        If "memory_cache" is defined, get_context method is cached
        for 9 minutes and 30 seconds to optimize db accesses without return
        stale oauth credentials.
        """
        placeholder = Placeholder.objects.create(slot="test")

        lti_consumer = LTIConsumerFactory()
        model_instance = add_plugin(
            placeholder,
            LTIConsumerPlugin,
            "en",
            url=lti_consumer.url,
            lti_provider_id=lti_consumer.lti_provider_id,
        )
        request = RequestFactory().get("/")
        request.user = AnonymousUser()
        request.session = {}
        request.toolbar = CMSToolbar(request)
        view_set = LTIConsumerViewsSet()

        with self.assertNumQueries(1):
            view_set.get_context(request, "v1.0", model_instance.pk)

        mock_params.assert_called_once()

        mock_params.reset_mock()

        with self.assertNumQueries(0):
            view_set.get_context(request, "v1.0", model_instance.pk)

        mock_params.assert_not_called()
Ejemplo n.º 11
0
    def test_lti_consumer_api_get_context_edit_all_permissions(
            self, mock_params):
        """
        A query with a user in edition mode and with all permissions required to change
        the plugin should get the instructor role.
        """
        user = UserFactory()
        page = create_i18n_page("A page")
        placeholder = page.placeholders.get(slot="maincontent")

        lti_consumer = LTIConsumerFactory()
        model_instance = add_plugin(
            placeholder,
            LTIConsumerPlugin,
            "en",
            url=lti_consumer.url,
            lti_provider_id=lti_consumer.lti_provider_id,
        )

        # Add all necessary model and object level permissions
        self.add_permission(user, "change_lticonsumer")
        self.add_permission(user, "change_page")
        PagePermission.objects.create(
            page=placeholder.page,
            user=user,
            can_add=False,
            can_change=True,
            can_delete=False,
            can_publish=False,
            can_move_page=False,
        )

        request = RequestFactory().get(
            f"/api/v1.0/plugins/lti-consumer/{model_instance.pk}/context/",
            {
                "user_id": user.username,
                "lis_person_sourcedid": user.username,
                "lis_person_name_given:": user.username,
                "lis_person_contact_email_primary": user.email,
            },
        )
        request.user = user
        request.session = {}
        request.toolbar = CMSToolbar(request)
        request.toolbar.edit_mode_active = True
        view_set = LTIConsumerViewsSet()

        response = view_set.get_context(request, "v1.0", model_instance.pk)
        self.assertEqual(response.status_code, 200)

        mock_params.assert_called_once_with(
            user_infos={
                "user_id": user.username,
                "lis_person_sourcedid": user.username,
                "lis_person_name_given:": user.username,
                "lis_person_contact_email_primary": user.email,
            },
            edit=True,
        )
Ejemplo n.º 12
0
    def test_lti_consumer_api_get_context_method_is_cached(self, mock_params):
        """
        If "memory_cache" is defined, get_context method is cached
        for 5 minutes to optimize db accesses without returning
        stale oauth credentials.
        """
        placeholder = Placeholder.objects.create(slot="test")

        lti_consumer = LTIConsumerFactory()
        model_instance = add_plugin(
            placeholder,
            LTIConsumerPlugin,
            "en",
            url=lti_consumer.url,
            lti_provider_id=lti_consumer.lti_provider_id,
        )
        request = RequestFactory().get("/")
        request.user = AnonymousUser()
        request.session = {}
        request.toolbar = CMSToolbar(request)
        view_set = LTIConsumerViewsSet()

        with self.assertNumQueries(1):
            view_set.get_context(request, "v1.0", model_instance.pk)

        mock_params.assert_called_once_with(edit=False)

        mock_params.reset_mock()

        with self.assertNumQueries(0):
            view_set.get_context(request, "v1.0", model_instance.pk)

        mock_params.assert_not_called()

        # Check that cache is set separately for each language
        translation.activate("fr")
        with self.assertNumQueries(1):
            view_set.get_context(request, "v1.0", model_instance.pk)

        mock_params.assert_called_once_with(edit=False)

        mock_params.reset_mock()

        with self.assertNumQueries(0):
            view_set.get_context(request, "v1.0", model_instance.pk)

        mock_params.assert_not_called()
        translation.deactivate()
Ejemplo n.º 13
0
    def test_lti_consumer_api_get_context_edit_missing_permissions(
            self, mock_params):
        """
        A query with a user in edition mode but with missing permissions to change
        the plugin should not get the instructor role.
        """
        user = UserFactory()
        page = create_i18n_page("A page")
        placeholder = page.placeholders.get(slot="maincontent")

        lti_consumer = LTIConsumerFactory()
        model_instance = add_plugin(
            placeholder,
            LTIConsumerPlugin,
            "en",
            url=lti_consumer.url,
            lti_provider_id=lti_consumer.lti_provider_id,
        )

        # Add all necessary model and object level permissions except one
        skip = random.choice(range(3))
        if skip != 0:
            self.add_permission(user, "change_lticonsumer")
        if skip != 1:
            self.add_permission(user, "change_page")
        if skip != 2:
            PagePermission.objects.create(
                page=placeholder.page,
                user=user,
                can_add=False,
                can_change=True,
                can_delete=False,
                can_publish=False,
                can_move_page=False,
            )

        request = RequestFactory().get(
            f"/api/v1.0/plugins/lti-consumer/{model_instance.pk}/context/")
        request.user = user
        request.session = {}
        request.toolbar = CMSToolbar(request)
        request.toolbar.edit_mode_active = True
        view_set = LTIConsumerViewsSet()

        response = view_set.get_context(request, "v1.0", model_instance.pk)
        self.assertEqual(response.status_code, 200)

        mock_params.assert_called_once_with(edit=False)
Ejemplo n.º 14
0
    def test_lti_consumer_api_get_context_is_automatic_resizing_default(
            self, _mock_params):
        """
        The "is_automatic_resizing" parameter should default to True if it is not set
        in the provider configuration.
        """
        page = create_i18n_page("A page")
        placeholder = page.placeholders.get(slot="maincontent")

        lti_consumer = LTIConsumerFactory()
        model_instance = add_plugin(
            placeholder,
            LTIConsumerPlugin,
            "en",
            url=lti_consumer.url,
            lti_provider_id=lti_consumer.lti_provider_id,
            is_automatic_resizing=lti_consumer.is_automatic_resizing,
        )

        # Set from settings
        is_automatic_param = random.choice([True, False])
        user_id = str(uuid.uuid4())
        with override_settings(RICHIE_LTI_PROVIDERS={
                "lti_provider_test": {
                    "is_automatic_resizing": is_automatic_param
                }
        }):
            response = self.client.get(
                f"/api/v1.0/plugins/lti-consumer/{model_instance.pk}/context/",
                {"user_id": user_id},
            )
        self.assertEqual(response.status_code, 200)
        content = json.loads(response.content)

        self.assertEqual(content["is_automatic_resizing"], is_automatic_param)

        # Set from the instance
        with override_settings(RICHIE_LTI_PROVIDERS={"lti_provider_test": {}}):
            response = self.client.get(
                f"/api/v1.0/plugins/lti-consumer/{model_instance.pk}/context/",
                {"user_id": user_id},
            )
        self.assertEqual(response.status_code, 200)
        content = json.loads(response.content)

        self.assertTrue(content["is_automatic_resizing"])
Ejemplo n.º 15
0
    def test_cms_plugins_get_lti_consumer_widget_props(self, _):
        """
        Verify that LTI content consumption parameters are correctly built through
        content_parameters wrapper
        """
        lti_consumer = LTIConsumerFactory()
        expected_content_parameters = {
            "lti_message_type":
            lti_consumer.lti_provider.get("display_name"),
            "lti_version":
            "LTI-1p0",
            "resource_link_id":
            str(lti_consumer.id),
            "context_id":
            "example.com",
            "user_id":
            "richie",
            "lis_person_contact_email_primary":
            "",
            "roles":
            "instructor",
            "oauth_consumer_key":
            lti_consumer.lti_provider.get("oauth_consumer_key"),
            "oauth_signature_method":
            "HMAC-SHA1",
            "oauth_timestamp":
            "1378916897",
            "oauth_nonce":
            "80966668944732164491378916897",
            "oauth_version":
            "1.0",
            "oauth_signature":
            "frVp4JuvT1mVXlxktiAUjQ7/1cw=",
        }
        expected_widget_props = {
            "url": lti_consumer.url,
            "content_parameters": expected_content_parameters,
            "automatic_resizing": True,
        }

        self.assertDictEqual(
            expected_widget_props,
            LTIConsumerPlugin.get_lti_consumer_widget_props(lti_consumer,
                                                            edit=True),
        )
Ejemplo n.º 16
0
    def test_lti_consumer_forms_shared_secret_placeholder(self):
        """
        The "form_shared_secret" should act as a proxy to the "shared_secret" field on the model
        and allow hiding the shared secret from the form after creation.
        """
        lti_consumer = LTIConsumerFactory(
            lti_provider_id=None,
            oauth_consumer_key="thisIsAtestOauthConsumerKey",
            shared_secret="thisIsAtestSharedSecret",
        )

        form = LTIConsumerForm(instance=lti_consumer)
        rendered = form.as_p()

        self.assertIn(
            ('<input type="password" name="form_shared_secret" '
             'value="%%shared_secret_placeholder%%" onfocus="this.value=&#x27;&#x27;" '
             'maxlength="50" id="id_form_shared_secret">'),
            rendered,
        )
        self.assertNotIn('id="shared_secret"', rendered)
        self.assertNotIn("thisIsAtestSharedSecret", rendered)

        # Submitting the placeholder value for the secret should not
        # impact the field on the model
        data = form.initial
        data["form_shared_secret"] = "%%shared_secret_placeholder%%"

        form = LTIConsumerForm(instance=lti_consumer, data=data)
        form.is_valid()
        self.assertTrue(form.is_valid())

        form.save()
        lti_consumer.refresh_from_db()
        self.assertEqual(lti_consumer.shared_secret, "thisIsAtestSharedSecret")

        # Submitting a new secret should update the corresponding field on the model
        data["form_shared_secret"] = "NewSharedSecret"
        form = LTIConsumerForm(instance=lti_consumer, data=data)
        form.is_valid()
        self.assertTrue(form.is_valid())

        form.save()
        lti_consumer.refresh_from_db()
        self.assertEqual(lti_consumer.shared_secret, "NewSharedSecret")
Ejemplo n.º 17
0
 def test_lti_consumer_factories_create_without_lti_provider(self):
     """The url field should be set to a random value for a custom provider."""
     lti_consumer = LTIConsumerFactory(lti_provider_id=None)
     self.assertIsNotNone(lti_consumer.url)
Ejemplo n.º 18
0
 def test_factories_lti_consumer_create_success(self):
     """Factory creation success."""
     lti_consumer = LTIConsumerFactory()
     self.assertIsNotNone(lti_consumer.url)