def test_return_url_with_messages(self): ''' Should generate a return url with messages. ''' tp = create_tp() self.assertIsNone(tp.build_return_url()) tp = create_tp(lp={ 'launch_presentation_return_url': 'http://foo.edu/done' }) self.assertEqual(tp.build_return_url(), 'http://foo.edu/done') tp = create_tp(lp={ 'launch_presentation_return_url': 'http://foo.edu/done', 'lti_errormsg': 'user error', 'lti_errorlog': 'lms error', 'lti_msg': 'user message', 'lti_log': 'lms message' }) return_url = tp.build_return_url() parsed = urlsplit(return_url) self.assertEqual(parsed.hostname, 'foo.edu') self.assertEqual(parsed.path, '/done') self.assertEqual(parse_qs(parsed.query), { 'lti_errormsg': 'user error', 'lti_errorlog': 'lms error', 'lti_msg': 'user message', 'lti_log': 'lms message' })
def test_launch_request_with_qs(self): """ test that qs params in launch url are ok """ launch_params = { 'lti_version': 'abc', 'lti_message_type': 'def', 'resource_link_id': '123' } tc = ToolConsumer('client_key', 'client_secret', launch_url='http://example.edu/foo?bar=1', params=launch_params) launch_req = tc.generate_launch_request(nonce='wxyz7890', timestamp='2345678901') got = parse_qs(unquote(launch_req.body)) correct = launch_params.copy() correct.update({ 'oauth_nonce': 'wxyz7890', 'oauth_timestamp': '2345678901', 'oauth_version': '1.0', 'oauth_signature_method': 'HMAC-SHA1', 'oauth_consumer_key': 'client_key', 'oauth_signature': 'UH2l86Wq/g5Mu64GpCRcec6tEYY=', }) self.assertEqual(got, correct)
def test_generate_launch_request(self): launch_params = { 'lti_version': 'foo', 'lti_message_type': 'bar', 'resource_link_id': 'baz' } tc = ContentItemResponse('client_key', 'client_secret', launch_url='http://example.edu/', params=launch_params) launch_req = tc.generate_launch_request(nonce='abcd1234', timestamp='1234567890') self.assertIsInstance(launch_req, PreparedRequest) got = parse_qs(unquote(launch_req.body.decode('utf-8'))) correct = launch_params.copy() correct.update({ 'oauth_nonce': 'abcd1234', 'oauth_timestamp': '1234567890', 'oauth_version': '1.0', 'oauth_signature_method': 'HMAC-SHA1', 'oauth_consumer_key': 'client_key', 'oauth_signature': 'u2xlj 1gF4y 6gKHNeiL9cN3tOI=', }) self.assertEqual(got, correct)
def test_generate_launch_request(self): launch_params = { 'lti_version': 'foo', 'lti_message_type': 'bar', 'resource_link_id': 'baz' } tc = ToolConsumer('client_key', 'client_secret', launch_url='http://example.edu/', params=launch_params) launch_req = tc.generate_launch_request(nonce='abcd1234', timestamp='1234567890') self.assertIsInstance(launch_req, PreparedRequest) got = parse_qs(unquote(launch_req.body)) correct = launch_params.copy() correct.update({ 'oauth_nonce': 'abcd1234', 'oauth_timestamp': '1234567890', 'oauth_version': '1.0', 'oauth_signature_method': 'HMAC-SHA1', 'oauth_consumer_key': 'client_key', 'oauth_signature': 'u2xlj 1gF4y 6gKHNeiL9cN3tOI=', }) self.assertEqual(got, correct)
def test_launch_request_with_qs(self): """ test that qs params in launch url are ok """ launch_params = { 'lti_version': 'abc', 'lti_message_type': 'def', 'resource_link_id': '123' } tc = ContentItemResponse('client_key', 'client_secret', launch_url='http://example.edu/foo?bar=1', params=launch_params) launch_req = tc.generate_launch_request(nonce='wxyz7890', timestamp='2345678901') got = parse_qs(unquote(launch_req.body.decode('utf-8'))) correct = launch_params.copy() correct.update({ 'oauth_nonce': 'wxyz7890', 'oauth_timestamp': '2345678901', 'oauth_version': '1.0', 'oauth_signature_method': 'HMAC-SHA1', 'oauth_consumer_key': 'client_key', 'oauth_signature': 'UH2l86Wq/g5Mu64GpCRcec6tEYY=', }) self.assertEqual(got, correct)
def lti_launch(self, lti_consumer, lti_resource_link_id, assignment_uuid=None, query_assignment_uuid=None, custom_puid=None, nonce=None, timestamp=None, follow_redirects=True, **kwargs): launch_url = "http://localhost/api/lti/auth" launch_params = kwargs.copy() launch_params['resource_link_id'] = lti_resource_link_id if assignment_uuid: launch_params['custom_assignment'] = assignment_uuid if custom_puid: launch_params['custom_puid'] = custom_puid if query_assignment_uuid: launch_url = launch_url + "?assignment=" + query_assignment_uuid tool_consumer = ToolConsumer(lti_consumer.oauth_consumer_key, lti_consumer.oauth_consumer_secret, params=launch_params, launch_url=launch_url) launch_request = tool_consumer.generate_launch_request( nonce=nonce, timestamp=timestamp) launch_data = parse_qs(launch_request.body.decode('utf-8')) rv = self.client.post('/api/lti/auth', data=launch_data, follow_redirects=follow_redirects) yield rv rv.close()
def test_return_url_with_messages(self): ''' Should generate a return url with messages. ''' tp = create_tp() self.assertIsNone(tp.build_return_url()) tp = create_tp( lp={'launch_presentation_return_url': 'http://foo.edu/done'}) self.assertEqual(tp.build_return_url(), 'http://foo.edu/done') tp = create_tp( lp={ 'launch_presentation_return_url': 'http://foo.edu/done', 'lti_errormsg': 'user error', 'lti_errorlog': 'lms error', 'lti_msg': 'user message', 'lti_log': 'lms message' }) return_url = tp.build_return_url() parsed = urlsplit(return_url) self.assertEqual(parsed.hostname, 'foo.edu') self.assertEqual(parsed.path, '/done') self.assertEqual( parse_qs(parsed.query), { 'lti_errormsg': 'user error', 'lti_errorlog': 'lms error', 'lti_msg': 'user message', 'lti_log': 'lms message' })
def _get_membership_ext(cls, lti_context): lti_consumer = lti_context.lti_consumer memberships_id = lti_context.ext_ims_lis_memberships_id memberships_url = lti_context.ext_ims_lis_memberships_url params = { 'id': memberships_id, 'lti_message_type':'basic-lis-readmembershipsforcontext', 'lti_version': 'LTI-1p0', 'oauth_callback': 'about:blank' } request = requests.Request('POST', memberships_url, data=params).prepare() sign = OAuth1(lti_consumer.oauth_consumer_key, lti_consumer.oauth_consumer_secret, signature_type=SIGNATURE_TYPE_BODY, signature_method=SIGNATURE_HMAC) signed_request = sign(request) params = parse_qs(signed_request.body.decode('utf-8')) data = LTIMembership._post_membership_request(memberships_url, params) root = ElementTree.fromstring(data.encode('utf-8')) codemajor = root.find('statusinfo/codemajor') if codemajor is not None and codemajor.text in ['Failure', 'Unsupported']: raise MembershipInvalidRequestException if root.find('memberships') == None or len(root.findall('memberships/member')) == 0: raise MembershipNoResultsException members = [] for record in root.findall('memberships/member'): roles_text = record.findtext('roles') member = { 'user_id': record.findtext('user_id'), 'roles': roles_text.split(",") if roles_text != None else [], 'global_unique_identifier': None, 'student_number': None, 'lis_result_sourcedid': record.findtext('lis_result_sourcedid'), 'person_contact_email_primary': record.findtext('person_contact_email_primary'), 'person_name_given': record.findtext('person_name_given'), 'person_name_family': record.findtext('person_name_family'), 'person_name_full': record.findtext('person_name_full') } # find global unique identifier if available if lti_consumer.global_unique_identifier_param and record.findtext(lti_consumer.global_unique_identifier_param): member['global_unique_identifier'] = record.findtext(lti_consumer.global_unique_identifier_param) # find student number if available if lti_consumer.student_number_param and record.findtext(lti_consumer.student_number_param): member['student_number'] = record.findtext(lti_consumer.student_number_param) members.append(member) return members
def lti_launch(self, lti_consumer, lti_resource_link_id, assignment_uuid=None, query_assignment_uuid=None, nonce=None, timestamp=None, follow_redirects=True, invalid_launch=False, **kwargs): launch_url = "http://localhost/api/lti/auth" oauth_signature = kwargs.pop('oauth_signature', None) launch_params = kwargs.copy() launch_params['resource_link_id'] = lti_resource_link_id if assignment_uuid: launch_params['custom_assignment'] = assignment_uuid if query_assignment_uuid: launch_url = launch_url+"?assignment="+query_assignment_uuid # add basic required launch parameters if not 'lti_version' in launch_params: launch_params['lti_version'] = "LTI-1p0" if not 'lti_message_type' in launch_params: launch_params['lti_message_type'] = "basic-lti-launch-request" if 'roles' in launch_params and launch_params.get('roles') == None: launch_params.pop('roles') tool_consumer = ToolConsumer( lti_consumer.oauth_consumer_key, lti_consumer.oauth_consumer_secret, params=launch_params, launch_url=launch_url ) # overwrite lti_version and lti_message_type if needed (set by lti.LaunchParams) if 'lti_version' in launch_params and launch_params.get('lti_version') == None: tool_consumer.launch_params._params.pop('lti_version') if 'lti_message_type' in launch_params and launch_params.get('lti_message_type') == None: tool_consumer.launch_params._params.pop('lti_message_type') if invalid_launch: with mock.patch.object(ToolConsumer, 'has_required_params', return_value=True): launch_request = tool_consumer.generate_launch_request(nonce=nonce, timestamp=timestamp) else: launch_request = tool_consumer.generate_launch_request(nonce=nonce, timestamp=timestamp) launch_data = parse_qs(launch_request.body.decode('utf-8')) # overwrite oauth_signature for tests if invalid_launch and oauth_signature: launch_data['oauth_signature'] = oauth_signature rv = self.client.post('/api/lti/auth', data=launch_data, follow_redirects=follow_redirects) yield rv rv.close()
def test_error_redirect(self): from lti.contrib.django import DjangoToolProvider tp = create_tp( lp={'launch_presentation_return_url': 'http://example.edu/bar'}, tp_class=DjangoToolProvider) redirect_retval = tp.error_redirect(errormsg='abcd', errorlog='efgh') self.assertEqual(redirect_retval, 'foo') redirect_url, = mock.shortcuts.redirect.call_args[0] parsed = urlsplit(redirect_url) self.assertEqual(parse_qs(parsed.query), { 'lti_errormsg': 'abcd', 'lti_errorlog': 'efgh' })
def test_success_redirect(self): from lti.contrib.django import DjangoToolProvider tp = create_tp( lp={'launch_presentation_return_url': 'http://example.edu/foo'}, tp_class=DjangoToolProvider) redirect_retval = tp.success_redirect(msg='bar', log='baz') self.assertEqual(redirect_retval, 'foo') redirect_url, = mock.shortcuts.redirect.call_args[0] parsed = urlsplit(redirect_url) self.assertEqual(parse_qs(parsed.query), { 'lti_msg': 'bar', 'lti_log': 'baz' })
def test_success_redirect(self): from lti.contrib.django import DjangoToolProvider tp = create_tp(lp={ 'launch_presentation_return_url': 'http://example.edu/foo' }, tp_class=DjangoToolProvider) redirect_retval = tp.success_redirect(msg='bar', log='baz') self.assertEqual(redirect_retval, 'foo') redirect_url, = mock.shortcuts.redirect.call_args[0] parsed = urlsplit(redirect_url) self.assertEqual(parse_qs(parsed.query), { 'lti_msg': 'bar', 'lti_log': 'baz' })
def test_error_redirect(self): from lti.contrib.django import DjangoToolProvider tp = create_tp(lp={ 'launch_presentation_return_url': 'http://example.edu/bar' }, tp_class=DjangoToolProvider) redirect_retval = tp.error_redirect(errormsg='abcd', errorlog='efgh') self.assertEqual(redirect_retval, 'foo') redirect_url, = mock.shortcuts.redirect.call_args[0] parsed = urlsplit(redirect_url) self.assertEqual(parse_qs(parsed.query), { 'lti_errormsg': 'abcd', 'lti_errorlog': 'efgh' })
def _get_membership_ext(cls, lti_context): lti_consumer = lti_context.lti_consumer memberships_id = lti_context.ext_ims_lis_memberships_id memberships_url = lti_context.ext_ims_lis_memberships_url params = { 'id': memberships_id, 'lti_message_type':'basic-lis-readmembershipsforcontext', 'lti_version': 'LTI-1p0', 'oauth_callback': 'about:blank' } request = requests.Request('POST', memberships_url, data=params).prepare() sign = OAuth1(lti_consumer.oauth_consumer_key, lti_consumer.oauth_consumer_secret, signature_type=SIGNATURE_TYPE_BODY, signature_method=SIGNATURE_HMAC) signed_request = sign(request) params = parse_qs(signed_request.body.decode('utf-8')) data = LTIMembership._post_membership_request(memberships_url, params) root = ElementTree.fromstring(data.encode('utf-8')) codemajor = root.find('statusinfo/codemajor') if codemajor is not None and codemajor.text in ['Failure', 'Unsupported']: raise MembershipInvalidRequestException if root.find('memberships') == None or len(root.findall('memberships/member')) == 0: raise MembershipNoResultsException members = [] for record in root.findall('memberships/member'): roles_text = record.findtext('roles') member = { 'user_id': record.findtext('user_id'), 'roles': roles_text.split(",") if roles_text != None else [], 'lis_result_sourcedid': record.findtext('lis_result_sourcedid'), 'person_contact_email_primary': record.findtext('person_contact_email_primary'), 'person_name_given': record.findtext('person_name_given'), 'person_name_family': record.findtext('person_name_family'), 'person_name_full': record.findtext('person_name_full') } # override user_id with user_id_override if present in membership result if lti_consumer.user_id_override and record.findtext(lti_consumer.user_id_override) is not None: member['user_id'] = record.findtext(lti_consumer.user_id_override) members.append(member) return members
def lti_launch(self, lti_consumer, lti_resource_link_id, assignment_uuid=None, nonce=None, timestamp=None, follow_redirects=True, **kwargs): launch_params = kwargs.copy() launch_params['resource_link_id'] = lti_resource_link_id if assignment_uuid: launch_params['custom_assignment'] = assignment_uuid tool_consumer = ToolConsumer( lti_consumer.oauth_consumer_key, lti_consumer.oauth_consumer_secret, params=launch_params, #launch_url not actually used. Just needed for validation launch_url='http://localhost/api/lti/auth' ) launch_request = tool_consumer.generate_launch_request(nonce=nonce, timestamp=timestamp) launch_data = parse_qs(launch_request.body.decode('utf-8')) rv = self.client.post('/api/lti/auth', data=launch_data, follow_redirects=follow_redirects) yield rv rv.close()
def _get_membership(cls, lti_consumer, memberships_id, memberships_url): params = { 'id': memberships_id, 'lti_message_type':'basic-lis-readmembershipsforcontext', 'lti_version': 'LTI-1p0', 'oauth_callback': 'about:blank' } request = requests.Request('POST', memberships_url, data=params).prepare() sign = OAuth1(lti_consumer.oauth_consumer_key, lti_consumer.oauth_consumer_secret, signature_type=SIGNATURE_TYPE_BODY, signature_method=SIGNATURE_HMAC) signed_request = sign(request) params = parse_qs(signed_request.body.decode('utf-8')) xmlString = LTIMembership._send_membership_request(memberships_url, params) root = ElementTree.fromstring(xmlString) codemajor = root.find('statusinfo/codemajor') if codemajor is not None and codemajor.text in ['Failure', 'Unsupported']: raise MembershipInvalidRequestException if root.find('memberships') == None or len(root.findall('memberships/member')) == 0: raise MembershipNoResultsException members = [] for member in root.findall('memberships/member'): roles_text = member.findtext('roles') members.append({ 'user_id': member.findtext('user_id'), 'roles': roles_text.split(",") if roles_text != None else [], 'person_contact_email_primary': member.findtext('person_contact_email_primary'), 'person_name_given': member.findtext('person_name_given'), 'person_name_family': member.findtext('person_name_family'), 'person_name_full': member.findtext('person_name_full') }) return members
def lti_launch(self, lti_consumer, lti_resource_link_id, assignment_uuid=None, query_assignment_uuid=None, nonce=None, timestamp=None, follow_redirects=True, invalid_launch=False, **kwargs): launch_url = "http://localhost/api/lti/auth" oauth_signature = kwargs.pop('oauth_signature', None) launch_params = kwargs.copy() launch_params['resource_link_id'] = lti_resource_link_id if assignment_uuid: launch_params['custom_assignment'] = assignment_uuid if query_assignment_uuid: launch_url = launch_url + "?assignment=" + query_assignment_uuid # add basic required launch parameters if not 'lti_version' in launch_params: launch_params['lti_version'] = "LTI-1p0" if not 'lti_message_type' in launch_params: launch_params['lti_message_type'] = "basic-lti-launch-request" if 'roles' in launch_params and launch_params.get('roles') == None: launch_params.pop('roles') tool_consumer = ToolConsumer(lti_consumer.oauth_consumer_key, lti_consumer.oauth_consumer_secret, params=launch_params, launch_url=launch_url) # overwrite lti_version and lti_message_type if needed (set by lti.LaunchParams) if 'lti_version' in launch_params and launch_params.get( 'lti_version') == None: tool_consumer.launch_params._params.pop('lti_version') if 'lti_message_type' in launch_params and launch_params.get( 'lti_message_type') == None: tool_consumer.launch_params._params.pop('lti_message_type') if invalid_launch: with mock.patch.object(ToolConsumer, 'has_required_params', return_value=True): launch_request = tool_consumer.generate_launch_request( nonce=nonce, timestamp=timestamp) else: launch_request = tool_consumer.generate_launch_request( nonce=nonce, timestamp=timestamp) launch_data = parse_qs(launch_request.body.decode('utf-8')) # overwrite oauth_signature for tests if invalid_launch and oauth_signature: launch_data['oauth_signature'] = oauth_signature rv = self.client.post('/api/lti/auth', data=launch_data, follow_redirects=follow_redirects) yield rv rv.close()
def _get_membership_ext(cls, lti_context): lti_consumer = lti_context.lti_consumer memberships_id = lti_context.ext_ims_lis_memberships_id memberships_url = lti_context.ext_ims_lis_memberships_url params = { 'id': memberships_id, 'lti_message_type': 'basic-lis-readmembershipsforcontext', 'lti_version': 'LTI-1p0', 'oauth_callback': 'about:blank' } request = requests.Request('POST', memberships_url, data=params).prepare() sign = OAuth1(lti_consumer.oauth_consumer_key, lti_consumer.oauth_consumer_secret, signature_type=SIGNATURE_TYPE_BODY, signature_method=SIGNATURE_HMAC) signed_request = sign(request) params = parse_qs(signed_request.body.decode('utf-8')) data = LTIMembership._post_membership_request(memberships_url, params) root = ElementTree.fromstring(data.encode('utf-8')) codemajor = root.find('statusinfo/codemajor') if codemajor is not None and codemajor.text in [ 'Failure', 'Unsupported' ]: raise MembershipInvalidRequestException if root.find('memberships') == None or len( root.findall('memberships/member')) == 0: raise MembershipNoResultsException members = [] for record in root.findall('memberships/member'): roles_text = record.findtext('roles') member = { 'user_id': record.findtext('user_id'), 'roles': roles_text.split(",") if roles_text != None else [], 'global_unique_identifier': None, 'student_number': None, 'lis_result_sourcedid': record.findtext('lis_result_sourcedid'), 'person_contact_email_primary': record.findtext('person_contact_email_primary'), 'person_name_given': record.findtext('person_name_given'), 'person_name_family': record.findtext('person_name_family'), 'person_name_full': record.findtext('person_name_full') } # find global unique identifier if available if lti_consumer.global_unique_identifier_param and record.findtext( lti_consumer.global_unique_identifier_param): member['global_unique_identifier'] = record.findtext( lti_consumer.global_unique_identifier_param) if lti_consumer.custom_param_regex_sanitizer and lti_consumer.global_unique_identifier_param.startswith( 'custom_'): regex = re.compile( lti_consumer.custom_param_regex_sanitizer) member['global_unique_identifier'] = regex.sub( '', member['global_unique_identifier']) if member['global_unique_identifier'] == '': member['global_unique_identifier'] = None # find student number if available if lti_consumer.student_number_param and record.findtext( lti_consumer.student_number_param): member['student_number'] = record.findtext( lti_consumer.student_number_param) if lti_consumer.custom_param_regex_sanitizer and lti_consumer.student_number_param.startswith( 'custom_'): regex = re.compile( lti_consumer.custom_param_regex_sanitizer) member['student_number'] = regex.sub( '', member['student_number']) if member['student_number'] == '': member['student_number'] = None members.append(member) return members