-
Notifications
You must be signed in to change notification settings - Fork 99
Fix LTI 1.1 Basic Outcomes Service and LTI 2.0 Result Service to Support External User IDs #310
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -93,7 +93,7 @@ | |
| "/projects/open-edx-building-and-running-a-course/en/latest/exercises_tools/lti_component.html" | ||
| "'>" | ||
| ) | ||
| RESULT_SERVICE_SUFFIX_PARSER = re.compile(r"^user/(?P<anon_id>\w+)", re.UNICODE) | ||
| RESULT_SERVICE_SUFFIX_PARSER = re.compile(r"^user/(?P<anon_id>[\w-]+)", re.UNICODE) | ||
| LTI_1P1_ROLE_MAP = { | ||
| 'student': 'Student,Learner', | ||
| 'staff': 'Administrator', | ||
|
|
@@ -835,7 +835,7 @@ def external_user_id(self): | |
|
|
||
| def get_lti_1p1_user_id(self): | ||
| """ | ||
| Returns the user ID to send to an LTI tool during an LTI 1.1 launch. If the | ||
| Returns the user ID to send to an LTI tool during an LTI 1.1/2.0 launch. If the | ||
| enable_external_user_id_1p1_launches CourseWaffleFlag is enabled for the course, returns the external_user_id | ||
| defined by the external_user_ids Djangoapp. Otherwise, returns the anonymous_user_id. | ||
|
|
||
|
|
@@ -849,6 +849,23 @@ def get_lti_1p1_user_id(self): | |
|
|
||
| return self.anonymous_user_id | ||
|
|
||
| def get_lti_1p1_user_from_user_id(self, user_id): | ||
| """ | ||
| Returns the user object associated with a user_id. This is used in LTI 1.1/2.0 integrations for calls to the | ||
| LTI 1.1 Basic Outcomes service and the LTI 2.0 Results service. Tool Providers may make calls to this library's | ||
| endpoints with a user identifier. This function returns a user object associated with that user identifier. | ||
|
|
||
| The user identifier may be a course-anonymized user ID (i.e. the anonymous_user_id) or the global, consistent | ||
| user ID (i.e. the external_user_id). This functions returns the correct User object. | ||
| """ | ||
| if external_user_id_1p1_launches_enabled(self.scope_ids.usage_id.context_key): | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is right, based on #249.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah that looks like the sort of replacement done there: https://github.com/openedx/xblock-lti-consumer/pull/249/files#diff-6e408862179502a0ac809fab914e028b18311a17a3b0874469bd6416bc4a449fL73
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since the IDs are different formats we could detect that difference, but this seems cleaner |
||
| try: | ||
| return compat.get_user_from_external_user_id(user_id) | ||
| except LtiError: | ||
| return None | ||
| else: | ||
| return self.runtime.service(self, 'user').get_user_by_anonymous_id(user_id) | ||
|
|
||
| @property | ||
| def resource_link_id(self): | ||
| """ | ||
|
|
@@ -1293,7 +1310,7 @@ def result_service_handler(self, request, suffix=''): | |
| except LtiError: | ||
| return Response(status=401) # Unauthorized in this case. 401 is right | ||
|
|
||
| user = self.runtime.service(self, 'user').get_user_by_anonymous_id(anon_id) | ||
| user = self.get_lti_1p1_user_from_user_id(anon_id) | ||
| if not user: # that means we can't save to database, as we do not have real user id. | ||
| msg = _("[LTI]: Real user not found against anon_id: {}").format(anon_id) | ||
| log.info(msg) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -183,8 +183,9 @@ def handle_request(self, request): | |
| log.debug("[LTI]: %s", error_message) | ||
| return response_xml_template.format(**failure_values) | ||
|
|
||
| anon_id = unquote(sourced_id.split(':')[-1]) | ||
| real_user = self.xblock.runtime.service(self, 'user').get_user_by_anonymous_id(anon_id) | ||
| user_id = unquote(sourced_id.split(':')[-1]) | ||
| real_user = self.xblock.get_lti_1p1_user_from_user_id(user_id) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice variable name change here |
||
|
|
||
| if not real_user: # that means we can't save to database, as we do not have real user id. | ||
| failure_values['imsx_messageIdentifier'] = escape(imsx_message_identifier) | ||
| failure_values['imsx_description'] = "User not found." | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adds support for UUID user IDs. Otherwise, it lops off part of the user ID.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this must have been maddening to find