Fix LTI 1.1 Basic Outcomes Service and LTI 2.0 Result Service to Support External User IDs - #310
Conversation
4829b43 to
212455f
Compare
| "'>" | ||
| ) | ||
| 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) |
There was a problem hiding this comment.
This adds support for UUID user IDs. Otherwise, it lops off part of the user ID.
There was a problem hiding this comment.
this must have been maddening to find
Codecov ReportBase: 97.78% // Head: 97.76% // Decreases project coverage by
Additional details and impacted files@@ Coverage Diff @@
## master #310 +/- ##
==========================================
- Coverage 97.78% 97.76% -0.03%
==========================================
Files 72 72
Lines 5875 5910 +35
==========================================
+ Hits 5745 5778 +33
- Misses 130 132 +2
Flags with carried forward coverage won't be shown. Click here to find out more.
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report at Codecov. |
| 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): |
There was a problem hiding this comment.
yeah that looks like the sort of replacement done there: https://github.com/openedx/xblock-lti-consumer/pull/249/files#diff-6e408862179502a0ac809fab914e028b18311a17a3b0874469bd6416bc4a449fL73
There was a problem hiding this comment.
since the IDs are different formats we could detect that difference, but this seems cleaner
| @@ -17,6 +17,15 @@ Unreleased | |||
| ~~~~~~~~~~ | |||
|
|
|||
| ======= | |||
There was a problem hiding this comment.
this set of ==== shouldn't be in the changelog, I left it in there when I was resolving conflicts
😞
| 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) |
There was a problem hiding this comment.
nice variable name change here
ashultz0
left a comment
There was a problem hiding this comment.
👍 shipit
after shipping please capture those test instructions into a doc ideally in the repo or at least in the wiki, they're very useful
212455f to
403b452
Compare
… support external user IDs In #307, we added the ability to send a stable, static user identifier (i.e. external user ID) to fix failed launches with the QwikLabs tool. This is because the QwikLabs tool did not work with the course-anonymized user IDs we used to send (i.e. anonymous user IDs). Inadvertently, this change broke the LTI 1.1 Basic Outcomes Service and the LTI 2.0 Result Service for courses that use the external user ID (i.e. they have the lti_consumer.enable_external_user_id_1p1_launches CourseWaffleFlag enabled). The Basic Outcomes Service and Result Service handle grade pass backs. Because we now have two ways to identify a user in LTI 1.1/2.0, we must update the Basic Outcomes Service and Result Service to support both. This commit fixes this bug.
403b452 to
3bbbdd5
Compare
Description
In #307, we added the ability to send a stable, static user identifier (i.e. external user ID) to fix failed launches with the QwikLabs tool. This is because the QwikLabs tool did not work with the course-anonymized user IDs we used to send (i.e. anonymous user IDs). Inadvertently, this change broke the LTI 1.1 Basic Outcomes Service and the LTI 2.0 Result Service for courses that use the external user ID (i.e. they have the
lti_consumer.enable_external_user_id_1p1_launchesCourseWaffleFlagenabled). The Basic Outcomes Service and Result Service handle grade pass backs. Because we now have two ways to identify a user in LTI 1.1/2.0, we must update the Basic Outcomes Service and Result Service to support both.Testing Instructions
Testing these is a little complex. I've tried to be as comprehensive as I can be with the instructions here, but please reach out if you have trouble.
LTI 1.1 Basic Outcomes Service
Set up devstack.
Create an LTI 1.1 component in Studio, following the instructions in the README.
Set up your preferred API testing tool (e.g. Postman) to send a POST request.
sourcedIdXML component appropriately.The
basic_outcomes_urlcan be retrieved viewing the live LTI component in the LMS, if you’re using the SaLTIre tool, because it displays this value for you. If it does not, you can retrieve it by using the LTI rest endpoints view.https://<LMS_DOMAIN>/courses/<COURSE_ID>/lti_rest_endpoints/, find your component, and select thelti_1_1_result_service_xml_endpoint. Note that you should usehttpwith devstack.Open a Python shell and install the
oauthlibPython module.xblock-lti-consumer.Open a Python shell and enter the following commands.
client_keyandclient_secretcome from your LTI passport string. The ones below come from the SaLTIre testing tool, as described in thexblock-lti-consumerREADME file.oauth_body_hashnot matching, make sure that yourxml_bodymatches the body variable inverify_oauth_body_signature. A breakpoint can be helpful.headersshould look something like this.{'Content-Type': 'text/xml', 'Authorization': 'OAuth oauth_nonce="5609288327616222561669665375", oauth_timestamp="1669665375", oauth_version="1.0", oauth_signature_method="HMAC-SHA1", oauth_consumer_key="test", oauth_body_hash="vAVegN28HcixFW7OuHgfx0Ld%2Bdk%3D", oauth_signature="4Or9QJKG66jFHpZU6JeyNHcYdDk%3D"'}LTI 2.0 Result Service
To send a Result Service request in devstack, follow these steps.
Set up devstack.
Create an LTI 1.1 component in Studio, following the instructions in the README.
Set up your preferred API testing tool (e.g. Postman) to send a PUT/GET request.
{ "@context" : "http://purl.imsglobal.org/ctx/lis/v2/Result", "@type" : "Result", "resultScore" : 0.83, "comment" : "This is exceptional work." }The
result_service_urlcan be retrieved viewing the live LTI component in the LMS, if you’re using the SaLTIre tool, because it displays this value for you. If it does not, you can retrieve it by using the LTI rest endpoints view.https://<LMS_DOMAIN>/courses/<COURSE_ID>/lti_rest_endpoints/, find your component, and select thelti_2_0_result_service_json_endpoint. Note that you should usehttpwith devstack.Content Typeheader toapplication/vnd.ims.lis.v2.result+json.Open a Python shell and install the
oauthlibPython module.xblock-lti-consumer.Open a Python shell and enter the following commands.
client_keyandclient_secretcome from your LTI passport string. The ones below come from the SaLTIre testing tool, as described in thexblock-lti-consumerREADME file.oauth_body_hashnot matching, make sure that yourxml_bodymatches the body variable inverify_oauth_body_signature. A breakpoint can be helpful.json_bodymatches exactly what is in Postman, including whitespace characters (e.g. spaces versus tabs).headersshould look something like this.{'Content-Type': 'text/xml', 'Authorization': 'OAuth oauth_nonce="5609288327616222561669665375", oauth_timestamp="1669665375", oauth_version="1.0", oauth_signature_method="HMAC-SHA1", oauth_consumer_key="test", oauth_body_hash="vAVegN28HcixFW7OuHgfx0Ld%2Bdk%3D", oauth_signature="4Or9QJKG66jFHpZU6JeyNHcYdDk%3D"'}Manual Testing
I performed the following manual tests.
lti_consumer.enable_external_user_id_1p1_launchesflag on, sent Basic Outcomes Service request with external user IDlti_consumer.enable_external_user_id_1p1_launchesflag off, sent Basic Outcomes Service request with anonymous user IDlti_consumer.enable_external_user_id_1p1_launchesflag on, sent Result Service request with external user IDlti_consumer.enable_external_user_id_1p1_launchesflag off, sent Result Service request with anonymous user ID