Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

POC for course tool launch #386

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion lti_consumer/lti_1p3/consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -885,8 +885,13 @@ def generate_launch_request(
self.set_extra_claim(self.get_assessment_control_claim())
elif launch_data.message_type == "LtiEndAssessment":
proctoring_claims = self.get_end_assessment_claims()
elif launch_data.message_type == "LtiResourceLinkRequest":
proctoring_claims = {}
else:
raise ValueError('lti_message_hint must \"LtiStartProctoring\" or \"LtiEndAssessment\".')
raise ValueError(
'lti_message_hint must be \"LtiStartProctoring\" or \"LtiEndAssessment\"'
'or \"LtiResourceLinkRequest\"'
)

self.set_extra_claim(proctoring_claims)

Expand Down
8 changes: 7 additions & 1 deletion lti_consumer/plugin/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
LtiNrpsContextMembershipViewSet, access_token_endpoint,
deep_linking_content_endpoint, deep_linking_response_endpoint,
launch_gate_endpoint, public_keyset_endpoint,
start_proctoring_assessment_endpoint)
start_proctoring_assessment_endpoint,
initiate_course_tool_launch_endpoint)

# LTI 1.3 APIs router
router = routers.SimpleRouter(trailing_slash=False)
Expand All @@ -21,6 +22,11 @@

app_name = 'lti_consumer'
urlpatterns = [
re_path(
rf'lti_consumer/v1/lti/(?P<lti_config_id>[-\w]+)/resource/{settings.USAGE_ID_PATTERN}/initiate_login',
initiate_course_tool_launch_endpoint,
name='lti_consumer.initiate_course_tool_launch_endpoint'
),
path(
'lti_consumer/v1/public_keysets/<uuid:lti_config_id>',
public_keyset_endpoint,
Expand Down
44 changes: 42 additions & 2 deletions lti_consumer/plugin/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied, ValidationError
from django.db import transaction
from django.http import Http404, JsonResponse
from django.shortcuts import render
from django.shortcuts import redirect, render
from django.utils.crypto import get_random_string
from django.views.decorators.clickjacking import xframe_options_exempt, xframe_options_sameorigin
from django.views.decorators.csrf import csrf_exempt
Expand All @@ -18,7 +18,7 @@
from edx_django_utils.cache import TieredCache, get_cache_key
from jwkest.jwt import JWT, BadSyntax
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import UsageKey
from opaque_keys.edx.keys import CourseKey, UsageKey
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
Expand All @@ -44,6 +44,7 @@
LtiNrpsContextMembershipBasicSerializer,
LtiNrpsContextMembershipPIISerializer)
from lti_consumer.lti_1p3.extensions.rest_framework.utils import IgnoreContentNegotiation
from lti_consumer.data import Lti1p3LaunchData
from lti_consumer.models import LtiAgsLineItem, LtiConfiguration, LtiDlContentItem
from lti_consumer.plugin import compat
from lti_consumer.signals.signals import LTI_1P3_PROCTORING_ASSESSMENT_STARTED
Expand Down Expand Up @@ -82,6 +83,45 @@ def has_block_access(user, block, course_key):
return course_access and block_access


@require_http_methods(["GET"])
def initiate_course_tool_launch_endpoint(request, usage_id, lti_config_id):
"""
Launch an LTI tool configured platform wide with a given course_id as the learning context.
"""
try:
lti_config = LtiConfiguration.objects.get(config_id=lti_config_id)

if lti_config.version != lti_config.LTI_1P3:
raise LtiError(
"LTI Error: LTI 1.1 configurations not supported."
)
except (LtiError, ObjectDoesNotExist):
raise Http404 from exc

# Tools configured with a specific placement cannot be launched course wide
# Mostly a hack in place of properly modeling 'platform wide' configurations
if lti_config.location:
raise PermissionDenied

location = UsageKey.from_string(usage_id)
course_key = location.course_key

# Problem. How can we get the user's role for the passed in course/context?
# We have to use a compat layer to do this which gets messy because this isn't
# just needed in the LMS but also exams.
user_role = None
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is really problematic when it comes to running this library in anything other than the LMS. We need to ask the running service for user permissions.


consumer = lti_config.get_lti_consumer()
launch_data = Lti1p3LaunchData(
user_id=request.user.id,
config_id=lti_config.config_id,
context_id=str(course_key),
resource_link_id=str(location),
user_role=user_role,
)

return redirect(consumer.prepare_preflight_url(launch_data))

@require_http_methods(["GET"])
def public_keyset_endpoint(request, usage_id=None, lti_config_id=None):
"""
Expand Down