def test_consumer(self): with self.silo['Consumers'].open() as collection: entity = collection.new_entity() consumer = lti.ToolConsumer.new_from_values( entity, self.cipher, 'default', key="12345", secret=ul("secret")) self.assertTrue(consumer.entity is entity) self.assertTrue(isinstance(consumer, lti.ToolConsumer)) self.assertTrue(consumer.entity['Handle'].value == 'default') self.assertTrue(consumer.entity['Key'].value == '12345') self.assertTrue(consumer.entity['Secret'].value == self.cipher.encrypt(b'secret')) # at this stage the entity has not been persisted but # the local attributes should be set... self.assertTrue(consumer.key == '12345') self.assertTrue(consumer.secret == 'secret') # now check persistence self.assertTrue(len(collection) == 0) collection.insert_entity(consumer.entity) self.assertTrue(len(collection) == 1) check_entity = collection.values()[0] check_consuemr = lti.ToolConsumer(check_entity, self.cipher) self.assertTrue(check_consuemr.entity['Handle'].value == 'default') self.assertTrue(check_consuemr.entity['Key'].value == '12345') self.assertTrue(check_consuemr.entity['Secret'].value == self.cipher.encrypt(b'secret')) self.assertTrue(check_consuemr.key == '12345') self.assertTrue(check_consuemr.secret == 'secret') # now check the update process consumer.update_from_values('updated', 'password') self.assertTrue(consumer.entity['Handle'].value == 'updated') self.assertTrue(consumer.entity['Key'].value == '12345') self.assertTrue(consumer.entity['Secret'].value == self.cipher.encrypt(b'password')) self.assertTrue(consumer.key == '12345') self.assertTrue(consumer.secret == 'password') self.assertTrue(len(collection) == 1) check_entity = collection.values()[0] check_consuemr = lti.ToolConsumer(check_entity, self.cipher) self.assertTrue(check_consuemr.entity['Handle'].value == 'updated') self.assertTrue(check_consuemr.entity['Key'].value == '12345') self.assertTrue(check_consuemr.entity['Secret'].value == self.cipher.encrypt(b'password')) self.assertTrue(check_consuemr.key == '12345') self.assertTrue(check_consuemr.secret == 'password')
def test_create_silo_option(self): class TPApp(lti.ToolProviderApp): private_files = self.d p = optparse.OptionParser() TPApp.add_options(p) options, args = p.parse_args(['-m']) # setup should succeed with default metadata TPApp.setup(options=options, args=args) # in memory database created, no silos with TPApp.container['Silos'].open() as collection: self.assertTrue(len(collection) == 0) class TPApp(lti.ToolProviderApp): private_files = self.d p = optparse.OptionParser() TPApp.add_options(p) options, args = p.parse_args(['-m', '--create_silo']) TPApp.setup(options=options, args=args) # in memory database created, with default silo with TPApp.container['Silos'].open() as collection: self.assertTrue(len(collection) == 1) silo = collection.values()[0] self.assertTrue(silo['Slug'].value == 'testing') with silo['Consumers'].open() as collection: self.assertTrue(len(collection) == 1) consumer = collection.values()[0] tc = lti.ToolConsumer(consumer, TPApp.new_app_cipher()) self.assertTrue(tc.key == '12345', tc.key) self.assertTrue(tc.secret == 'secret', tc.secret)
def test_set_launch_user(self): class TPApp(lti.ToolProviderApp): private_files = self.d p = optparse.OptionParser() TPApp.add_options(p) options, args = p.parse_args(['-m', '--create_silo']) TPApp.setup(options=options, args=args) with TPApp.container['Silos'].open() as collection: silo = collection.values()[0] with silo['Consumers'].open() as collection: consumer = collection.values()[0] tc = lti.ToolConsumer(consumer, TPApp.new_app_cipher()) app = TPApp() req = MockRequest() context = lti.ToolProviderContext(req.environ, req.start_response) context.session = wsgi.CookieSession() context.session.establish() context.consumer = tc # parameters will be empty, so no launch user app.set_launch_user(context) self.assertTrue(context.user is None) with TPApp.container['Users'].open() as collection: self.assertTrue(len(collection) == 0) # add a user to the parameters context.parameters['user_id'] = '123456' context.parameters['lis_person_name_given'] = 'Jane' context.parameters['lis_person_name_family'] = 'Doe' context.parameters['lis_person_name_full'] = 'Jane Doe' context.parameters['lis_person_contact_email_primary'] = \ '*****@*****.**' app.set_launch_user(context) with TPApp.container['Users'].open() as collection: self.assertTrue(len(collection) == 1) user = collection.values()[0] self.assertTrue(user['UserID'].value == "123456") self.assertTrue(user['GivenName'].value == "Jane") self.assertTrue(user['FamilyName'].value == "Doe") self.assertTrue(user['FullName'].value == "Jane Doe") self.assertTrue(user['Email'].value == "*****@*****.**") uconsumer = user['Consumer'].get_entity() # check it is associated with the consumer we created self.assertTrue(uconsumer == consumer) # and no visits with user['Visits'].open() as vcollection: self.assertTrue(len(vcollection) == 0) self.assertTrue(context.user == user)
def test_set_launch_group(self): class TPApp(lti.ToolProviderApp): private_files = self.d p = optparse.OptionParser() TPApp.add_options(p) options, args = p.parse_args(['-m', '--create_silo']) TPApp.setup(options=options, args=args) with TPApp.container['Silos'].open() as collection: silo = collection.values()[0] with silo['Consumers'].open() as collection: consumer = collection.values()[0] tc = lti.ToolConsumer(consumer, TPApp.new_app_cipher()) app = TPApp() req = MockRequest() context = lti.ToolProviderContext(req.environ, req.start_response) context.session = wsgi.CookieSession() context.session.establish() context.consumer = tc # parameters will be empty, so no launch group app.set_launch_group(context) self.assertTrue(context.group is None) with TPApp.container['Contexts'].open() as collection: self.assertTrue(len(collection) == 0) # add in group parameters context.parameters['context_id'] = "gid" context.parameters['context_type'] = "Group" context.parameters['context_title'] = "Group101" context.parameters['context_label'] = "G101" app.set_launch_group(context) # now check that we have a group in the data store with TPApp.container['Contexts'].open() as collection: self.assertTrue(len(collection) == 1) group = collection.values()[0] self.assertTrue(group['ContextID'].value == "gid") self.assertTrue(group['Title'].value == "Group101") self.assertTrue(group['Label'].value == "G101") self.assertTrue( group['Types'].value == "urn:lti:context-type:ims/lis/Group") gconsumer = group['Consumer'].get_entity() # check it is associated with the consumer we created self.assertTrue(gconsumer == consumer) # and there are no resources with group['Resources'].open() as rcollection: self.assertTrue(len(rcollection) == 0) self.assertTrue(context.group == group)
def consumers_page(self, context): page_context = self.new_page_context(context) # add errors errors = set(('duplicate_key', )) query = context.get_query() error = query.get('error', '') for e in errors: page_context[e] = (e == error) owner = context.session.get_owner() if owner is None: # we require an owner to be logged in raise wsgi.PageNotAuthorized page_context['user_name'] = owner['FullName'].value silo = owner['Silo'].get_entity() page_context['silo'] = silo consumer_list = [] with silo['Consumers'].open() as collection: collection.set_orderby( odata.Parser('Handle asc').parse_orderby_option()) for consumer in collection.itervalues(): citem = {} consumer = lti.ToolConsumer(consumer, self.app_cipher) query = urllib.urlencode({ 'cid': odata.ODataURI.format_literal(consumer.entity['ID']) }) citem['consumer'] = consumer citem['cedit_link'] = xml.escape_char_data7( 'edit?' + query, True) citem['cdel_link'] = xml.escape_char_data7( 'del?' + query, True) consumer_list.append(citem) query = urllib.urlencode( {'silo': odata.ODataURI.format_literal(silo['ID'])}) page_context['cadd_link'] = xml.escape_char_data7( 'add?' + query, True) page_context['consumers'] = consumer_list page_context[self.csrf_token] = context.session.sid() data = self.render_template(context, 'consumers/index.html', page_context) context.set_status(200) return self.html_response(context, data)
def test_constructor(self): class TPApp(lti.ToolProviderApp): private_files = self.d p = optparse.OptionParser() TPApp.add_options(p) options, args = p.parse_args(['-m', '--create_silo']) TPApp.setup(options=options, args=args) with TPApp.container['Silos'].open() as collection: self.assertTrue(len(collection) == 1) silo = collection.values()[0] with silo['Consumers'].open() as collection: consumer = collection.values()[0] lti.ToolConsumer(consumer, TPApp.new_app_cipher()) # ready to test the app app = TPApp() self.assertTrue(isinstance(app.provider, lti.ToolProvider)) # now test the App logic, initially no visits with TPApp.container['Visits'].open() as collection: self.assertTrue(len(collection) == 0)
def consumer_edit_action(self, context): if context.environ['REQUEST_METHOD'].upper() != 'POST': raise wsgi.MethodNotAllowed owner = context.session.get_owner() if owner is None: # we require an owner to be logged in raise wsgi.PageNotAuthorized silo = owner['Silo'].GetEntity() try: cid = context.get_form_long('cid') key = context.get_form_string('key', 80) secret = context.get_form_string('secret', 80) with silo['Consumers'].OpenCollection() as collection: consumer = lti.ToolConsumer(collection[cid], self.app_cipher) # we never change the handle consumer.update_from_values(key, secret) except ValueError: raise wsgi.BadRequest except KeyError: raise wsgi.PageNotAuthorized link = URI.from_octets("./").resolve(context.get_url()) return self.redirect_page(context, link, 303)
def consumer_edit_page(self, context): page_context = self.new_page_context(context) owner = context.session.get_owner() if owner is None: # we require an owner to be logged in raise wsgi.PageNotAuthorized page_context['owner'] = owner silo = owner['Silo'].GetEntity() page_context['silo'] = silo query = context.get_query() cid = odata.ParseURILiteral(query.get('cid', '')).value with silo['Consumers'].OpenCollection() as collection: try: consumer = lti.ToolConsumer(collection[cid], self.app_cipher) except KeyError: raise wsgi.PageNotAuthorized page_context['consumer'] = consumer page_context['cid_attr'] = xml.EscapeCharData7(str(cid), True) page_context[self.csrf_token] = context.session.sid() data = self.render_template(context, 'consumers/edit_form.html', page_context) context.set_status(200) return self.html_response(context, data)
def test_new_visit(self): class TPApp(lti.ToolProviderApp): private_files = self.d p = optparse.OptionParser() TPApp.add_options(p) options, args = p.parse_args(['-m', '--create_silo']) TPApp.setup(options=options, args=args) with TPApp.container['Silos'].open() as collection: silo = collection.values()[0] with silo['Consumers'].open() as collection: consumer = collection.values()[0] tc = lti.ToolConsumer(consumer, TPApp.new_app_cipher()) app = TPApp() req = MockRequest() context = lti.ToolProviderContext(req.environ, req.start_response) context.session = wsgi.CookieSession() unestablished_id = context.session.sid context.consumer = tc context.parameters['resource_link_id'] = 'rlink' context.parameters['resource_link_title'] = 'A Resource' context.parameters['resource_link_description'] = 'About the resource' app.set_launch_resource(context) # create a new visit in this unestablished session app.new_visit(context) with TPApp.container['Visits'].open() as collection: self.assertTrue(len(collection) == 1) visit = collection.values()[0] self.assertTrue(visit['Session'].value == unestablished_id) self.assertTrue(visit == context.visit) # now check that when we establish the session we're updated app.establish_session(context) self.assertTrue(context.session.established) established_id = context.session.sid self.assertTrue(unestablished_id != established_id) with TPApp.container['Visits'].open() as collection: self.assertTrue(len(collection) == 1) visit = collection.values()[0] self.assertTrue(visit['Session'].value == established_id) # but it should still be the same visit self.assertTrue(visit == context.visit) # now check that a new visit replaces an old one first_visit = visit context.parameters['user_id'] = '123456' context.parameters['lis_person_name_given'] = 'Jane' context.parameters['lis_person_name_family'] = 'Doe' context.parameters['lis_person_name_full'] = 'Jane Doe' context.parameters['lis_person_contact_email_primary'] = \ '*****@*****.**' app.set_launch_user(context) app.new_visit(context) self.assertFalse(first_visit == context.visit) self.assertTrue(context.visit['Session'].value == established_id) with TPApp.container['Visits'].open() as collection: self.assertTrue(len(collection) == 2) # reload the first visit first_visit = collection[first_visit.key()] # this visit should no longer be associated with a session self.assertTrue(first_visit['Session'].value is None) # now check that a new visit with a different user orphans # visits from the old user (even when resource doesn't match) janes_visit = context.visit context.parameters['resource_link_id'] = 'rlink2' context.parameters['resource_link_title'] = 'Another Resource' context.parameters['resource_link_description'] = 'Details' app.set_launch_resource(context) context.parameters['user_id'] = '123457' context.parameters['lis_person_name_given'] = 'John' context.parameters['lis_person_name_family'] = 'Doe' context.parameters['lis_person_name_full'] = 'John Doe' context.parameters['lis_person_contact_email_primary'] = \ '*****@*****.**' app.set_launch_user(context) app.new_visit(context) self.assertFalse(janes_visit == context.visit) with TPApp.container['Visits'].open() as collection: self.assertTrue(len(collection) == 3) # reload jane's visit janes_visit = collection[janes_visit.key()] # this visit should no longer be associated with a session self.assertTrue(janes_visit['Session'].value is None) # check that a login to a different resource with the same # user support multiple visits johns_first_visit = context.visit context.parameters['resource_link_id'] = 'rlink3' context.parameters['resource_link_title'] = 'Yet Another Resource' context.parameters['resource_link_description'] = 'More Details' app.set_launch_resource(context) app.new_visit(context) self.assertFalse(johns_first_visit == context.visit) with TPApp.container['Visits'].open() as collection: self.assertTrue(len(collection) == 4) # reload john's first visit johns_first_visit = collection[johns_first_visit.key()] # this visit should still be associated with the session self.assertTrue( johns_first_visit['Session'].value == established_id)
def test_set_launch_resource(self): class TPApp(lti.ToolProviderApp): private_files = self.d p = optparse.OptionParser() TPApp.add_options(p) options, args = p.parse_args(['-m', '--create_silo']) TPApp.setup(options=options, args=args) with TPApp.container['Silos'].open() as collection: silo = collection.values()[0] with silo['Consumers'].open() as collection: consumer = collection.values()[0] tc = lti.ToolConsumer(consumer, TPApp.new_app_cipher()) app = TPApp() req = MockRequest() context = lti.ToolProviderContext(req.environ, req.start_response) context.session = wsgi.CookieSession() context.session.establish() context.consumer = tc # parameters will be empty, so no launch resource: error try: app.set_launch_resource(context) self.fail("resource_link_id is required") except lti.LTIProtocolError: pass with TPApp.container['Resources'].open() as collection: self.assertTrue(len(collection) == 0) # add a resource link to the parameters context.parameters['resource_link_id'] = 'rlink' context.parameters['resource_link_title'] = 'A Resource' context.parameters['resource_link_description'] = 'About the resource' app.set_launch_resource(context) with TPApp.container['Resources'].open() as collection: self.assertTrue(len(collection) == 1) resource = collection.values()[0] self.assertTrue(resource['LinkID'].value == "rlink") self.assertTrue(resource['Title'].value == "A Resource") self.assertTrue( resource['Description'].value == "About the resource") rconsumer = resource['Consumer'].get_entity() # check it is associated with the consumer we created self.assertTrue(rconsumer == consumer) # and there are is no context self.assertTrue(resource['Context'].get_entity() is None) # and no visits with resource['Visits'].open() as vcollection: self.assertTrue(len(vcollection) == 0) self.assertTrue(context.resource == resource) # now check contexts too context.parameters['context_id'] = "gid" context.parameters['context_type'] = "Group" context.parameters['context_title'] = "Group101" context.parameters['context_label'] = "G101" context.parameters['resource_link_id'] = 'rlink2' context.parameters['resource_link_title'] = 'Another Resource' context.parameters['resource_link_description'] = 'Resource & context' app.set_launch_group(context) app.set_launch_resource(context) self.assertTrue(context.resource['LinkID'].value == 'rlink2') self.assertTrue( context.resource['Context'].get_entity() == context.group)