diff --git a/cms/djangoapps/contentstore/asset_storage_handlers.py b/cms/djangoapps/contentstore/asset_storage_handlers.py index 281258e03a8d..ad48638cde67 100644 --- a/cms/djangoapps/contentstore/asset_storage_handlers.py +++ b/cms/djangoapps/contentstore/asset_storage_handlers.py @@ -25,6 +25,7 @@ from common.djangoapps.util.json_request import JsonResponse from openedx.core.djangoapps.contentserver.caching import del_cached_content from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers +from openedx_filters.content_authoring.filters import LmsUrlCreationStarted from xmodule.contentstore.content import StaticContent # lint-amnesty, pylint: disable=wrong-import-order from xmodule.contentstore.django import contentstore # lint-amnesty, pylint: disable=wrong-import-order from xmodule.exceptions import NotFoundError # lint-amnesty, pylint: disable=wrong-import-order @@ -714,7 +715,21 @@ def get_asset_json(display_name, content_type, date, location, thumbnail_locatio Helper method for formatting the asset information to send to client. ''' asset_url = StaticContent.serialize_asset_key_with_slash(location) - external_url = urljoin(configuration_helpers.get_value('LMS_ROOT_URL', settings.LMS_ROOT_URL), asset_url) + lms_root = configuration_helpers.get_value('LMS_ROOT_URL', settings.LMS_ROOT_URL) + + try: + ## .. filter_implemented_name: LmsUrlCreationStarted + ## .. filter_type: org.openedx.course_authoring.lms.url.creation.started.v1 + lms_root = LmsUrlCreationStarted.run_filter( + context=lms_root, + org=location.org, + val_name='LMS_ROOT_URL', + default=settings.LMS_ROOT_URL + ) + except LmsUrlCreationStarted.PreventLmsUrlCreationRender as exc: + raise LmsUrlCreationNotAllowed(str(exc)) from exc + + external_url = urljoin(lms_root, asset_url) portable_url = StaticContent.get_static_path_from_location(location) usage_locations = [] if usage is None else usage return { @@ -732,3 +747,11 @@ def get_asset_json(display_name, content_type, date, location, thumbnail_locatio 'file_size': file_size, 'usage_locations': usage_locations, } + + +class LmsUrlCreationException(Exception): + pass + + +class LmsUrlCreationNotAllowed(LmsUrlCreationException): + pass diff --git a/common/djangoapps/util/course.py b/common/djangoapps/util/course.py index 721410ff6976..2188b5671e66 100644 --- a/common/djangoapps/util/course.py +++ b/common/djangoapps/util/course.py @@ -10,6 +10,7 @@ from opaque_keys.edx.keys import CourseKey, UsageKey from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers +from openedx_filters.content_authoring.filters import LmsUrlCreationStarted log = logging.getLogger(__name__) @@ -54,14 +55,36 @@ def get_link_for_about_page(course): elif settings.FEATURES.get('ENABLE_MKTG_SITE') and getattr(course, 'marketing_url', None): course_about_url = course.marketing_url else: + about_base = configuration_helpers.get_value('LMS_ROOT_URL', settings.LMS_ROOT_URL) + + try: + ## .. filter_implemented_name: LmsUrlCreationStarted + ## .. filter_type: org.openedx.course_authoring.lms.url.creation.started.v1 + about_base = LmsUrlCreationStarted.run_filter( + context=about_base, + org=course.id.org, + val_name='LMS_ROOT_URL', + default=settings.LMS_ROOT_URL + ) + except LmsUrlCreationStarted.PreventLmsUrlCreationRender as exc: + raise LmsUrlCreationNotAllowed(str(exc)) from exc + course_about_url = '{about_base_url}/courses/{course_key}/about'.format( - about_base_url=configuration_helpers.get_value('LMS_ROOT_URL', settings.LMS_ROOT_URL), + about_base_url=about_base, course_key=str(course.id), ) return course_about_url +class LmsUrlCreationException(Exception): + pass + + +class LmsUrlCreationNotAllowed(LmsUrlCreationException): + pass + + def has_certificates_enabled(course): """ Arguments: diff --git a/common/djangoapps/util/tests/test_filters.py b/common/djangoapps/util/tests/test_filters.py new file mode 100644 index 000000000000..d19dbf6780fe --- /dev/null +++ b/common/djangoapps/util/tests/test_filters.py @@ -0,0 +1,121 @@ +""" +Test that various filters are fired for models/views in the student app. +""" +from django.http import HttpResponse +from django.test import override_settings +from common.djangoapps.util import course +from openedx_filters import PipelineStep +from openedx_filters.content_authoring.filters import LmsUrlCreationStarted +from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase +from xmodule.modulestore.tests.factories import CourseFactory + +from common.djangoapps.util.course import LmsUrlCreationNotAllowed +from openedx.core.djangolib.testing.utils import skip_unless_lms + + +class TestLmsUrlCreationPipelineStep(PipelineStep): + """ + Utility class used when getting steps for pipeline. + """ + + def run_filter(self, context, org, val_name, default): # pylint: disable=arguments-differ + """Pipeline step that modifies lms url creation.""" + context = "https://lms-url-creation" + return { + "context": context + } + + +class TestLmsUrlCreationFilterPrevent(PipelineStep): + """ + Utility class used when getting steps for pipeline. + """ + + def run_filter(self, context, org, val_name, default): # pylint: disable=arguments-differ + """Pipeline step that changes dashboard view response before the dashboard is rendered.""" + response = HttpResponse("This is a custom response.") + raise LmsUrlCreationStarted.PreventLmsUrlCreationRender( + "Can't render lms url creation.", + response=response, + ) + + +@skip_unless_lms +class LmsUrlCreationStartedFiltersTest(ModuleStoreTestCase): + """ + Tests for the Open edX Filters associated with the lms url creation process. + This class guarantees that the following filters are triggered during the microsite render: + - LmsUrlCreationStarted + """ + + def setUp(self): # pylint: disable=arguments-differ + super().setUp() + self.course = CourseFactory.create() + self.org = "test" + self.val_name = 'LMS_ROOT_URL' + self.default = "https://lms-base" + + @override_settings( + OPEN_EDX_FILTERS_CONFIG={ + "org.openedx.course_authoring.lms.url.creation.started.v1": { + "pipeline": [ + "common.djangoapps.util.tests.test_filters.TestLmsUrlCreationPipelineStep", + ], + "fail_silently": False, + }, + }, + ) + def test_lms_url_creation_filter_executed(self): + """ + Test whether the lms url creation filter is triggered before the user's + render site process. + Expected result: + - LmsUrlCreationStarted is triggered and executes TestLmsUrlCreationPipelineStep. + - The arguments that the receiver gets are the arguments used by the filter. + """ + course_about_url = course.get_link_for_about_page(self.course) + + expected_course_about = '{about_base_url}/courses/{course_key}/about'.format( + about_base_url='https://lms-url-creation', + course_key=str(self.course.id), + ) + + self.assertEqual(expected_course_about, course_about_url) + + @override_settings( + OPEN_EDX_FILTERS_CONFIG={ + "org.openedx.course_authoring.lms.url.creation.started.v1": { + "pipeline": [ + "common.djangoapps.util.tests.test_filters.TestLmsUrlCreationFilterPrevent", + ], + "fail_silently": False, + }, + }, + ) + def test_lms_url_creation_filter_prevent(self): + """ + Test prevent the lms url creation filter through a pipeline step. + Expected result: + - TenantAwareLinkRenderStarted is triggered and executes TestLmsUrlCreationFilterPrevent. + - The user can't get lms url creation. + """ + with self.assertRaises(LmsUrlCreationNotAllowed): + course.get_link_for_about_page(self.course) + + @override_settings(OPEN_EDX_FILTERS_CONFIG={}, LMS_ROOT_URL="https://lms-base") + def test_enrollment_without_filter_configuration(self): + """ + Test usual get link for about page process, without filter's intervention. + Expected result: + - Returns the course sharing url, this can be one of course's social sharing url, marketing url, or + lms course about url. + - The get process ends successfully. + """ + course_about_url = course.get_link_for_about_page(self.course) + + expected_course_about = '{about_base_url}/courses/{course_key}/about'.format( + about_base_url='https://lms-base', + course_key=str(self.course.id), + ) + + self.assertEqual(expected_course_about, course_about_url) \ No newline at end of file diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 6225d5914001..1cc04aa82116 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -750,7 +750,7 @@ openedx-events==9.10.0 # edx-event-bus-redis # event-tracking # ora2 -openedx-filters==1.9.0 +git+https://github.com/eduNEXT/openedx-filters.git@jipm/add_tenant_aware_filter # via # -r requirements/edx/kernel.in # lti-consumer-xblock diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index cfcaeb3c7d86..15506b95b496 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -1291,7 +1291,7 @@ openedx-events==9.10.0 # edx-event-bus-redis # event-tracking # ora2 -openedx-filters==1.9.0 +git+https://github.com/eduNEXT/openedx-filters.git@jipm/add_tenant_aware_filter # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index 3cdca0fe84da..ba10e5a65c26 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -878,7 +878,7 @@ openedx-events==9.10.0 # edx-event-bus-redis # event-tracking # ora2 -openedx-filters==1.9.0 +git+https://github.com/eduNEXT/openedx-filters.git@jipm/add_tenant_aware_filter # via # -r requirements/edx/base.txt # lti-consumer-xblock diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 8349f0a076bb..c682bd06b882 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -957,7 +957,7 @@ openedx-events==9.10.0 # edx-event-bus-redis # event-tracking # ora2 -openedx-filters==1.9.0 +git+https://github.com/eduNEXT/openedx-filters.git@jipm/add_tenant_aware_filter # via # -r requirements/edx/base.txt # lti-consumer-xblock