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

[BD-24] [TNL-7318]: LTI AGS Implementation - LineItem Service #92

Merged

Conversation

giovannicimolin
Copy link
Contributor

@giovannicimolin giovannicimolin commented Jul 21, 2020

This PR implements the LTI AGS LineItem Service.

What does these changes do?
They are implementing the LTI AGS LineItem implementation and putting on the groundwork required to launch other LTI Advantage services.
The changes here include a partial shift to the LTI configuration being stored in a model, authentication methods and all things needed to get the LineItem service working.

Where is the LTI configuration stored?
In the XBlocks, we're only currently using a model as a proxy to the settings stored in the blocks.

What are the main architecture changes?
LineItems are stored on Django models and use DRF to provide a CRUD API for LineItems.
XBlock configuration is still stored on xblock fields, but it's accessed through a python API.

What are the next steps for this?
Chunk this change into manageable and easy to review pull requests:

  1. Proxying configuration through Django model (and a few small fixes). PR Merged: [BD-24] [TNL-7318] BB-2355: Move LTI configuration to models. #93
  2. Adding authentication and token verification support. PR Merged: [BD-24] [TNL-7318]: BB-2355 - Add decoding and scope verification methods #95
  3. Adding some Django support. PR Merged: [BD-24] [TNL-7318] BB-2355: Add LTI support and Django authentication extension.  #105
  4. Add LTI AGS endpoints. This one.

Testing instructions:

  1. Checkout this branch on your master's devstack src folder.
  2. Install it on lms and studio using pip install -e /edx/src/xblock-lti-consumer, apply migrations.
  3. Edit studio.yml and enable the following feature flag:
FEATURES:
    LTI_1P3_ENABLED: true
  1. Restart Studio (make studio-restart) and log in.
  2. Create a new course, add lti_consumer to the advanced modules list and add it to a course.
  3. Set LTI Version to LTI 1.3, and mark .
  4. Go to https://lti-ri.imsglobal.org/keygen/index and copy the public key, paste it into the LTI 1.3 Tool Public Key field. Save the private key for later.
  5. Generate a client token: go to www.jwt.io, select RS-256 and paste the private key in the private key file. Header and Body as follows and then copy the JWT.
# HEADER:
{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "123"
}

# PAYLOAD:
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "iat": 1516239022
}

  1. Retrieve an LTI Authentication Key: copy the OAuth Token URL and make the following request (don't forget to replace the JWT from the previous step). This will yield your access token to use the endpoints.
curl --request POST \
  --url 'http://localhost:18000/api/lti_consumer/v1/token/block-v1:OC+LTI+2020+type@lti_consumer+block@adfaf874726a4060808f04cb2bfaa6e2' \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data grant_type=client_credentials \
  --data client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
  --data client_assertion=PUT_YOUR_JWT_HERE \
  --data scope=https://purl.imsglobal.org/spec/lti-ags/scope/lineitem
  1. Go into Django admin, and copy the LTI Configuration ID (http://localhost:18000/admin/lti_consumer/lticonfiguration/).
  2. Make a request to the LTI AGS List endpoint:
curl --request GET \
  --url http://localhost:18000/api/lti_consumer/v1/lti/LTI_CONFIG_ID/lti-ags \
  --header 'authorization: Bearer ACCESS_TOKEN_HERE' 

# Should return []
  1. Create a new LineItem using POST:
curl --request POST \
  --url http://localhost:18000/api/lti_consumer/v1/lti/LTI_CONFIG_ID/lti-ags/ \
  --header 'authorization: Bearer ACCESS_TOKEN_HERE' \
  --header 'content-type: application/vnd.ims.lis.v2.lineitem+json' \
  --data '{"scoreMaximum": 100, "label": "Score", "tag": "score", "resourceId": "some-random-string"}
'
  1. Run step 9 again, and verify that the LineItem is retrieved.

(Optionally)
If you have a LTI 1.3 tool set up locally:
13. Perform a launch, and check that the LTI message includes the AGS claim:

# In the LTI message
  "https://purl.imsglobal.org/spec/lti-ags/claim/endpoint": {
    "scope": [
      "https://purl.imsglobal.org/spec/lti-ags/scope/lineitem",
      "https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly",
      "https://purl.imsglobal.org/spec/lti-ags/scope/score"
    ],
    "lineitems": "http://localhost:18000/api/lti_consumer/v1/lti/3/lti-ags"
  }

Certification suite:
To run the IMS certification suite properly, you'll need to override LMS_BASE_URL to match the ngrok url (so that the LTI AGS Claim contains the right URL).

Reviewers:

@openedx-webhooks
Copy link

openedx-webhooks commented Jul 21, 2020

Thanks for the pull request, @giovannicimolin! I've created BLENDED-489 to keep track of it in Jira. More details are on the BD-24 project page.

When this pull request is ready, tag your edX technical lead.

@openedx-webhooks openedx-webhooks added blended PR is managed through 2U's blended developmnt program needs triage labels Jul 21, 2020
@giovannicimolin giovannicimolin changed the title [BD-3] [TNL-7318]: LTI AGS PoC Implementation - Do not merge. WIP [BD-24] [TNL-7318]: LTI AGS PoC Implementation - Do not merge. WIP Jul 21, 2020
@giovannicimolin
Copy link
Contributor Author

giovannicimolin commented Sep 4, 2020

@nedbat Since the first two PRs supporting this one were merged, I'll pick this up next week, rebase, add tests and make sure it's fully compliant to the LTI spec (at least for the LineItem endpoint).

In the meantime, can you take a look at the architectural decisions I've taken here? I'm mostly concerned about these things:

  • Authentication class implementation (here): I feel like there's a better way to pass the LTI consumer back to the view without needing to re-instance it (and without storing it in a model).
  • Authorization is a custom permission class that makes use if the LTI consumer passed back through the request in the Authentication phase (here)
  • Pagination isn't implemented yet, and will need a custom class that complies with the LTI spec (I will split up a separate task to handle this)

References:
https://www.imsglobal.org/spec/lti-ags/v2p0#normative-references
https://www.imsglobal.org/spec/lti-ags/v2p0/#example-application-vnd-ims-lis-v2-lineitem-json-representation
https://www.imsglobal.org/spec/lti/v1p3/

@giovannicimolin giovannicimolin force-pushed the giovanni/bb-2355-add-lineitem-endpoint branch from 8074666 to 3a8aec4 Compare September 8, 2020 14:12
@giovannicimolin giovannicimolin changed the title [BD-24] [TNL-7318]: LTI AGS PoC Implementation - Do not merge. WIP [BD-24] [TNL-7318]: LTI AGS Implementation WIP Sep 11, 2020
@coveralls
Copy link

coveralls commented Sep 11, 2020

Coverage Status

Coverage decreased (-0.1%) to 98.792% when pulling 309b323 on open-craft:giovanni/bb-2355-add-lineitem-endpoint into 11152be on edx:master.

@giovannicimolin
Copy link
Contributor Author

giovannicimolin commented Sep 11, 2020

@nedbat At this point the main implementation is finished. I'm just writing tests (a lot of them) and cleaning it up.

@giovannicimolin giovannicimolin force-pushed the giovanni/bb-2355-add-lineitem-endpoint branch from 8d5360c to bcd36aa Compare September 15, 2020 18:56
@giovannicimolin giovannicimolin force-pushed the giovanni/bb-2355-add-lineitem-endpoint branch 2 times, most recently from acaca93 to 11fd22d Compare September 23, 2020 18:28
@giovannicimolin giovannicimolin changed the title [BD-24] [TNL-7318]: LTI AGS Implementation WIP [BD-24] [TNL-7318]: LTI AGS Implementation - LineItem Service Sep 29, 2020
@giovannicimolin
Copy link
Contributor Author

@viadanna Ready for review.

@giovannicimolin giovannicimolin force-pushed the giovanni/bb-2355-add-lineitem-endpoint branch from 4bc81ae to ef0cc83 Compare September 29, 2020 21:45
lti_consumer/utils.py Outdated Show resolved Hide resolved
Copy link
Contributor

@shimulch shimulch left a comment

Choose a reason for hiding this comment

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

LGTM 👍

  • I tested this as per testing instruction
  • I read through the code
    - [ ] I checked for accessibility issues
  • Includes documentation
    - [ ] I made sure any change in configuration variables is reflected in the corresponding client's configuration-secure repository.

Copy link
Contributor

@nedbat nedbat left a comment

Choose a reason for hiding this comment

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

Very nice. A few comments, I think only one point that could be a real problem.

lti_consumer/plugin/views.py Outdated Show resolved Hide resolved
lti_consumer/lti_1p3/tests/test_ags.py Outdated Show resolved Hide resolved
# LTI-AGS Scopes
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly',
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
]
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't have to add https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly and https://purl.imsglobal.org/spec/lti-ags/scope/score here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nedbat The results and score services are being implemented in separate tasks (https://openedx.atlassian.net/browse/TNL-7331 and https://openedx.atlassian.net/browse/TNL-7332) to keep the diff size smaller.

There's already a WIP PR out, based on this branch: #108. Which will be rebased when this lands.


Recreated here since we cannot import directly from
from the platform like so:
`from openedx.core.lib.api.serializers import UsageKeyField`
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if this means this code should go into the opaque-keys library? (Not now, but eventually.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nedbat Yes, this serializer should be moved from the core to opaque-keys.
If you prefer, I can move this to compat.py and mock the serializer in tests.
What do you say?

Copy link
Contributor

Choose a reason for hiding this comment

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

I am fine with merging this as is, and bringing up this need with the owners of opaque-keys to address.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nedbat Thanks for that! Can you ping the owners on this use case? I'm not sure if there's other places duplicating this code (or avoiding UsageKey serialization on plugins at all).

"endDateTime": "2018-04-06T22:05:03Z"
}

Note: The platform MUST NOT modify the 'resourceId', 'resourceLinkId' and 'tag' values.
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand what this means. Why would a serializer ever modify anything?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nedbat That was a straight copy from the IMS LTI-AGS spec (here). I've moved this to the model since it makes more sense to have it there.

@giovannicimolin
Copy link
Contributor Author

@shimulch @nedbat I've addressed your review comments.

@nedbat
Copy link
Contributor

nedbat commented Oct 5, 2020

I've successfully run the IMS Validator on this branch.

@nedbat
Copy link
Contributor

nedbat commented Oct 5, 2020

image

@nedbat nedbat merged commit 33bb7c7 into openedx:master Oct 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blended PR is managed through 2U's blended developmnt program merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants