Skip to content
Snippets Groups Projects
Commit 01a97d3c authored by Giovanni Cimolin da Silva's avatar Giovanni Cimolin da Silva
Browse files

fix: Fix studio permission checks when saving deep linking content.

parent 6491871b
No related branches found
No related tags found
No related merge requests found
......@@ -139,9 +139,11 @@ Instructions:
.. admonition:: Testing using ``ngrok``
When launching LTI 1.3 requests through ``ngrok``, make sure you set ``DCS_SESSION_COOKIE_SAMESITE = 'None'`` in your
``devstack.py`` (located in /edx/app/edxapp/edx-platform/(lms|cms)/envs``) when doing LTI 1.3 launches in the
devstack through ngrok. Do not forget to restart your services after updating the ``.py`` files.
When launching LTI 1.3 requests through ``ngrok``, make sure your LMS is serving session cookies marked as
``Secure`` and with the ``SameSite`` attribute set to ``None``. You can do this by changing ``SESSION_COOKIE_SECURE: true``
and ``DCS_SESSION_COOKIE_SAMESITE: None`` in your ``lms.yml`` configuration files. Note that this will break logins
for locally accessed URLs in the devstack.
Custom LTI Parameters
=====================
......@@ -367,6 +369,12 @@ Changelog
Please See the [releases tab](https://github.com/edx/xblock-lti-consumer/releases) for the complete changelog.
3.4.5 - 2022-03-16
------------------
* Fix LTI Deep Linking return endpoint permission checking method by replacing the old one with the proper
Studio API call.
3.4.4 - 2022-03-03
------------------
......
......@@ -4,4 +4,4 @@ Runtime will load the XBlock class from here.
from .apps import LTIConsumerApp
from .lti_xblock import LtiConsumerXBlock
__version__ = '3.4.4'
__version__ = '3.4.5'
......@@ -113,6 +113,18 @@ def user_has_access(*args, **kwargs):
return has_access(*args, **kwargs)
def user_has_studio_write_access(*args, **kwargs):
"""
Import and run `has_studio_write_access` from common modules.
Used to check if someone saving deep linking content has the
correct write permissions for a given.
"""
# pylint: disable=import-error,import-outside-toplevel
from common.djangoapps.student.auth import has_studio_write_access
return has_studio_write_access(*args, **kwargs)
def get_course_by_id(course_key):
"""
Import and run `get_course_by_id` from LMS
......
......@@ -63,13 +63,6 @@ from lti_consumer.utils import _
log = logging.getLogger(__name__)
def user_has_staff_access(user, course_key):
"""
Check if an user has write permissions to a given course.
"""
return compat.user_has_access(user, "staff", course_key)
def has_block_access(user, block, course_key):
"""
Checks if a user has access to given xblock.
......@@ -183,26 +176,31 @@ def access_token_endpoint(request, usage_id=None):
@require_http_methods(["POST"])
def deep_linking_response_endpoint(request, lti_config_id=None):
"""
Deep Linking response endpoint where tool can send back
Deep Linking response endpoint where tool can send back Deep Linking
content selected by instructions in the tool's UI.
For this feature to work, the LMS session cookies need to be Secure
and have the `SameSite` attribute set to `None`, otherwise we won't
be able to check user permissions.
"""
try:
# Retrieve LTI configuration
lti_config = LtiConfiguration.objects.get(id=lti_config_id)
# First, check if the user has sufficient permissions to
# save LTI Deep Linking content through the student.auth API.
course_key = lti_config.location.course_key
if not user_has_staff_access(request.user, course_key):
raise PermissionDenied()
# Get LTI consumer
lti_consumer = lti_config.get_lti_consumer()
# Retrieve Deep Linking return message and validate parameters
# Validate Deep Linking return message and return decoded message
content_items = lti_consumer.check_and_decode_deep_linking_token(
request.POST.get("JWT")
)
# Check if the user has sufficient permissions to
# save LTI Deep Linking content through the student.auth API.
course_key = lti_config.location.course_key
if not compat.user_has_studio_write_access(request.user, course_key):
raise PermissionDenied()
# On a transaction, clear older DeepLinking selections, then
# verify and save each content item passed from the tool.
with transaction.atomic():
......
......@@ -89,10 +89,17 @@ class LtiDeepLinkingResponseEndpointTestCase(LtiDeepLinkingTestCase):
super().setUp()
# Patch method that calls platform core to ask for user permissions
studio_access_patcher = patch('lti_consumer.plugin.views.user_has_staff_access')
self.addCleanup(studio_access_patcher.stop)
self._mock_has_studio_write_acess = studio_access_patcher.start()
self._mock_has_studio_write_acess.return_value = True
compat_mock = patch("lti_consumer.signals.compat")
self.addCleanup(compat_mock.stop)
self._compat_mock = compat_mock.start()
self._compat_mock.user_has_studio_write_access.return_value = True
has_studio_write_acess_patcher = patch(
'lti_consumer.plugin.views.compat.user_has_studio_write_access',
return_value=True
)
self.addCleanup(has_studio_write_acess_patcher.stop)
self._mock_has_studio_write_acess = has_studio_write_acess_patcher.start()
# Deep Linking response endpoint
self.url = '/lti_consumer/v1/lti/{}/lti-dl/response'.format(self.lti_config.id)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment