-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add initial version of LineItem Implementation
Signed-off-by: Giovanni Cimolin da Silva <[email protected]>
- Loading branch information
1 parent
425d5c2
commit acaca93
Showing
33 changed files
with
1,211 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
""" | ||
LTI Advantage Assignments and Grades service implementation | ||
""" | ||
|
||
|
||
class LtiAgs: | ||
""" | ||
LTI Advantage Consumer | ||
Implements LTI Advantage Services and ties them in | ||
with the LTI Consumer. This only handles the LTI | ||
message claim inclusion and token handling. | ||
Available services: | ||
* Assignments and Grades services (partial support) | ||
Reference: https://www.imsglobal.org/lti-advantage-overview | ||
""" | ||
def __init__( | ||
self, | ||
lineitems_url, | ||
allow_creating_lineitems=True, | ||
results_service_enabled=True, | ||
scores_service_enabled=True | ||
): | ||
""" | ||
Instance class with LTI AGS Global settings. | ||
""" | ||
# If the platform allows creating lineitems, set this | ||
# to True. | ||
self.allow_creating_lineitems = allow_creating_lineitems | ||
|
||
# Result and scores services | ||
self.results_service_enabled = results_service_enabled | ||
self.scores_service_enabled = scores_service_enabled | ||
|
||
# Lineitems urls | ||
self.lineitems_url = lineitems_url | ||
|
||
def get_available_scopes(self): | ||
""" | ||
Retrieves list of available token scopes in this instance. | ||
""" | ||
scopes = [] | ||
|
||
if self.allow_creating_lineitems: | ||
scopes.append('https://purl.imsglobal.org/spec/lti-ags/scope/lineitem') | ||
else: | ||
scopes.append('https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly') | ||
|
||
if self.results_service_enabled: | ||
scopes.append('https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly') | ||
|
||
if self.scores_service_enabled: | ||
scopes.append('https://purl.imsglobal.org/spec/lti-ags/scope/score') | ||
|
||
return scopes | ||
|
||
def get_lti_ags_launch_claim(self): | ||
""" | ||
Returns LTI AGS Claim to be injected in the LTI launch message. | ||
""" | ||
ags_claim = { | ||
"https://purl.imsglobal.org/spec/lti-ags/claim/endpoint": { | ||
"scope": self.get_available_scopes(), | ||
"lineitems": self.lineitems_url, | ||
} | ||
} | ||
|
||
return ags_claim |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
75 changes: 75 additions & 0 deletions
75
lti_consumer/lti_1p3/extensions/rest_framework/authentication.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
""" | ||
Django REST Framework extensions for LTI 1.3 & LTI Advantage implementation. | ||
Implements a custom authentication class to be used by LTI Advantage extensions. | ||
""" | ||
from django.utils.translation import ugettext as _ | ||
from rest_framework import authentication | ||
from rest_framework import exceptions | ||
|
||
from lti_consumer.models import LtiConfiguration | ||
|
||
|
||
class Lti1p3ApiAuthentication(authentication.BaseAuthentication): | ||
""" | ||
LTI 1.3 Token based authentication. | ||
Clients should authenticate by passing the token key in the "Authorization". | ||
LTI 1.3 expects a token like the following: | ||
Authorization: Bearer jwt-token | ||
Since the base implementation of this library uses JWT tokens, we expect | ||
a RSA256 signed token that contains the allowed scopes. | ||
""" | ||
keyword = 'Bearer' | ||
|
||
def authenticate(self, request): | ||
""" | ||
Authenticate an LTI 1.3 Tool. | ||
This doesn't return a user, but let's the external access and commit | ||
changes. | ||
TODO: Consider creating an user for LTI operations, both to keep track | ||
of changes and to use Django's authorization flow. | ||
""" | ||
auth = request.headers.get('Authorization', '').split() | ||
lti_config_id = request.parser_context['kwargs'].get('lti_config_id') | ||
|
||
# Check if auth token is present on request and is correctly formatted. | ||
if not auth or auth[0].lower() != self.keyword.lower(): | ||
msg = _('Missing LTI 1.3 authentication token.') | ||
raise exceptions.AuthenticationFailed(msg) | ||
|
||
if len(auth) == 1: | ||
msg = _('Invalid token header. No credentials provided.') | ||
raise exceptions.AuthenticationFailed(msg) | ||
|
||
if len(auth) > 2: | ||
msg = _('Invalid token header. Token string should not contain spaces.') | ||
raise exceptions.AuthenticationFailed(msg) | ||
|
||
# Retrieve LTI configuration or fail if it doesn't exist | ||
try: | ||
lti_configuration = LtiConfiguration.objects.get(pk=lti_config_id) | ||
lti_consumer = lti_configuration.get_lti_consumer() | ||
except: | ||
msg = _('LTI configuration not found.') | ||
raise exceptions.AuthenticationFailed(msg) | ||
|
||
# Verify token validity | ||
# This doesn't validate specific permissions, just checks if the token | ||
# is valid or not. | ||
try: | ||
lti_consumer.check_token(auth[1]) | ||
except: | ||
msg = _('Invalid token signature.') | ||
raise exceptions.AuthenticationFailed(msg) | ||
|
||
# Passing parameters back to the view through the request. | ||
# Not exactly optimal. | ||
request.lti_configuration = lti_configuration | ||
request.lti_consumer = lti_consumer | ||
|
||
# Return (None, None) since this isn't tied to any authentication | ||
# backend on Django, and it's just used for LTI endpoints. | ||
return (None, None) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
""" | ||
LTI 1.3 Django extensions - Content parsers | ||
Used by DRF views to render content in LTI APIs. | ||
""" | ||
|
||
from rest_framework import parsers | ||
|
||
|
||
class LineItemParser(parsers.JSONParser): | ||
""" | ||
Line Item Parser. | ||
It's the same as JSON parser, but uses a custom media_type. | ||
""" | ||
media_type = 'application/vnd.ims.lis.v2.lineitem+json' |
44 changes: 44 additions & 0 deletions
44
lti_consumer/lti_1p3/extensions/rest_framework/permissions.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
""" | ||
Django REST Framework extensions for LTI 1.3 & LTI Advantage implementation. | ||
Implements a custom authorization classes to be used by any of the | ||
LTI Advantage extensions. | ||
""" | ||
from rest_framework import permissions | ||
|
||
|
||
class LtiAgsPermissions(permissions.BasePermission): | ||
""" | ||
LTI AGS Permissions. | ||
This checks if the token included in the request | ||
has the allowed scopes to read/write LTI AGS items | ||
(LineItems, Results, Score). | ||
LineItem scopes: https://www.imsglobal.org/spec/lti-ags/v2p0#scope-and-allowed-http-methods | ||
Results: Not implemented yet. | ||
Score: Not implemented yet. | ||
""" | ||
def has_permission(self, request, view): | ||
# Retrieves token from request, which was already checked by | ||
# the Authentication class, so we assume it's a sane value. | ||
auth_token = request.headers.get('Authorization', '').split()[1] | ||
|
||
if view.action in ['list', 'retrieve']: | ||
# We don't need to wrap this around a try-catch because | ||
# the token was already tested by the Authentication class. | ||
has_perm = request.lti_consumer.check_token( | ||
auth_token, | ||
[ | ||
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly', | ||
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem', | ||
], | ||
) | ||
return has_perm | ||
elif view.action in ['create', 'update', 'partial_update', 'delete']: | ||
has_perm = request.lti_consumer.check_token( | ||
auth_token, | ||
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem' | ||
) | ||
return has_perm | ||
return False |
28 changes: 28 additions & 0 deletions
28
lti_consumer/lti_1p3/extensions/rest_framework/renderers.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
""" | ||
LTI 1.3 Django extensions - Content renderers | ||
Used by DRF views to render content in LTI APIs. | ||
""" | ||
from rest_framework import renderers | ||
|
||
|
||
class LineItemsRenderer(renderers.JSONRenderer): | ||
""" | ||
Line Items Renderer. | ||
It's a JSON parser, but uses a custom media_type. | ||
Reference: https://www.imsglobal.org/spec/lti-ags/v2p0#media-types-and-schemas | ||
""" | ||
media_type = 'application/vnd.ims.lis.v2.lineitemcontainer+json' | ||
format = 'json' | ||
|
||
|
||
class LineItemRenderer(renderers.JSONRenderer): | ||
""" | ||
Line Item Renderer. | ||
It's a JSON parser, but uses a custom media_type. | ||
Reference: https://www.imsglobal.org/spec/lti-ags/v2p0#media-types-and-schemas | ||
""" | ||
media_type = 'application/vnd.ims.lis.v2.lineitem+json' | ||
format = 'json' |
Empty file.
Empty file.
Oops, something went wrong.