-
Notifications
You must be signed in to change notification settings - Fork 35
/
lti.py
202 lines (161 loc) · 6.08 KB
/
lti.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
from django.conf import settings
import six
from pylti.common import (
LTIException, LTINotInSessionException, LTI_SESSION_KEY,
verify_request_common, LTIRoleException, LTI_ROLES)
from lti_auth.models import LTICourseContext
class LTI(object):
"""
LTI Object represents abstraction of current LTI session. It provides
callback methods and methods that allow developer to inspect
LTI basic-launch-request.
This object is instantiated by the LTIMixin
"""
def __init__(self, request_type, role_type):
self.request_type = request_type
self.role_type = role_type
self.lti_params = {}
def consumer_user_id(self): # pylint: disable=no-self-use
"""
Returns user_id as provided by LTI
"""
return "%s-%s" % \
(self.lti_params['oauth_consumer_key'],
self.lti_params['user_id'])
def user_identifier(self):
if 'custom_canvas_user_login_id' in self.lti_params:
return self.lti_params['custom_canvas_user_login_id']
def user_email(self):
"""
Returns user email as provided by LTI
"""
if 'lis_person_contact_email_primary' in self.lti_params:
return self.lti_params['lis_person_contact_email_primary']
return None
def user_fullname(self):
"""
Returns user's full name as provided by LTI
"""
if ('lis_person_name_full' in self.lti_params and
len(self.lti_params['lis_person_name_full']) > 0):
return self.lti_params['lis_person_name_full']
if 'user_id' in self.lti_params:
return self.lti_params['user_id']
return ''
def user_roles(self): # pylint: disable=no-self-use
"""
LTI roles of the authenticated user
:return: roles
"""
if 'roles' in self.lti_params:
return self.lti_params.get('roles', None).lower().split(',')
return []
def is_administrator(self):
return 'administrator' in self.lti_params.get('roles', '').lower()
def is_instructor(self):
roles = self.lti_params.get('roles', '').lower()
return 'instructor' in roles or 'staff' in roles
def course_context(self):
if 'context_id' in self.lti_params:
return self.lti_params.get('context_id')
def course_title(self):
if 'context_title' in self.lti_params:
return self.lti_params.get('context_title')
def sis_course_id(self):
if 'lis_course_offering_sourcedid' in self.lti_params:
return self.lti_params.get('lis_course_offering_sourcedid')
def canvas_domain(self):
if 'custom_canvas_api_domain' in self.lti_params:
return self.lti_params.get('custom_canvas_api_domain')
def custom_course_context(self):
"""
Returns the custom LTICourseContext id as provided by LTI
throws: KeyError or ValueError or LTICourseContext.DoesNotExist
:return: context -- the LTICourseContext instance or None
"""
return LTICourseContext.objects.get(
enable=True,
uuid=self.lti_params['custom_course_context'])
def clear_session(self, request):
"""
Invalidate the session
"""
if LTI_SESSION_KEY in request.session:
request.session[LTI_SESSION_KEY] = False
def verify(self, request):
"""
Verify if LTI request is valid, validation
depends on arguments
:raises: LTIException
"""
if self.request_type == 'session':
self._verify_session(request)
elif self.request_type == 'initial':
self._verify_request(request)
elif self.request_type == 'any':
self._verify_any(request)
else:
raise LTIException("Unknown request type")
return True
def _verify_any(self, request):
"""
Verify that request is in session or initial request
:raises: LTIException
"""
try:
self._verify_session(request)
except LTINotInSessionException:
self._verify_request(request)
@staticmethod
def _verify_session(request):
"""
Verify that session was already created
:raises: LTIException
"""
if not request.session.get(LTI_SESSION_KEY, False):
raise LTINotInSessionException('Session expired or unavailable')
def _verify_request(self, request):
"""
Verify LTI request
:raises: LTIException is request validation failed
"""
if request.method == 'POST':
params = dict(six.iteritems(request.POST))
else:
params = dict(six.iteritems(request.GET))
try:
verify_request_common(self._consumers(),
request.build_absolute_uri(),
request.method, request.META,
params)
self._validate_role()
self.lti_params = params
request.session[LTI_SESSION_KEY] = True
return True
except LTIException:
self.lti_params = {}
request.session[LTI_SESSION_KEY] = False
raise
def _consumers(self):
"""
Gets consumer's map from config
:return: consumers map
"""
config = getattr(settings, 'PYLTI_CONFIG', dict())
consumers = config.get('consumers', dict())
return consumers
def _validate_role(self):
"""
Check that user is in accepted/specified role
:exception: LTIException if user is not in roles
"""
if self.role_type != u'any':
if self.role_type in LTI_ROLES:
role_list = LTI_ROLES[self.role_type]
# find the intersection of the roles
roles = set(role_list) & set(self.user_roles())
if len(roles) < 1:
raise LTIRoleException('Not authorized.')
else:
raise LTIException("Unknown role {}.".format(self.role_type))
return True