Add role parameter to LTI. BLD-583.
This commit is contained in:
@@ -466,3 +466,20 @@ def _has_staff_access_to_descriptor(user, descriptor, course_context=None):
|
||||
descriptor: something that has a location attribute
|
||||
"""
|
||||
return _has_staff_access_to_location(user, descriptor.location, course_context)
|
||||
|
||||
|
||||
def get_user_role(user, course_id):
|
||||
"""
|
||||
Return corresponding string if user has staff, instructor or student
|
||||
course role in LMS.
|
||||
"""
|
||||
from courseware.courses import get_course
|
||||
course = get_course(course_id)
|
||||
if is_masquerading_as_student(user):
|
||||
return 'student'
|
||||
elif has_access(user, course, 'instructor'):
|
||||
return 'instructor'
|
||||
elif has_access(user, course, 'staff'):
|
||||
return 'staff'
|
||||
else:
|
||||
return 'student'
|
||||
|
||||
@@ -56,6 +56,17 @@ Feature: LMS.LTI component
|
||||
And I see in the gradebook table that "Total" is "5"
|
||||
|
||||
#7
|
||||
Scenario: Graded LTI component in LMS role's masquerading correctly works
|
||||
Given the course has correct LTI credentials with registered Instructor
|
||||
And the course has an LTI component with correct fields:
|
||||
| open_in_a_new_page | has_score |
|
||||
| False | True |
|
||||
And I view the LTI and it is rendered in iframe
|
||||
And I see in iframe that LTI role is Instructor
|
||||
And I switch to Student view
|
||||
Then I see in iframe that LTI role is Student
|
||||
|
||||
#8
|
||||
Scenario: Graded LTI component in LMS is correctly works with beta testers
|
||||
Given the course has correct LTI credentials with registered BetaTester
|
||||
And the course has an LTI component with correct fields:
|
||||
@@ -65,5 +76,3 @@ Feature: LMS.LTI component
|
||||
And I click on the "Progress" tab
|
||||
Then I see text "Problem Scores: 5/10"
|
||||
And I see graph with total progress "5%"
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#pylint: disable=C0111
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import pytz
|
||||
from mock import patch
|
||||
from pytz import UTC
|
||||
from nose.tools import assert_equal
|
||||
from splinter.exceptions import ElementDoesNotExist
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.urlresolvers import reverse
|
||||
@@ -12,9 +14,13 @@ from lettuce.django import django_url
|
||||
|
||||
from common import course_id, visit_scenario_item
|
||||
from courseware.tests.factories import InstructorFactory, BetaTesterFactory
|
||||
|
||||
from courseware.access import has_access
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
from nose.tools import assert_equals
|
||||
from common import course_id, visit_scenario_item
|
||||
|
||||
|
||||
@step('I view the LTI and error is shown$')
|
||||
def lti_is_not_rendered(_step):
|
||||
@@ -66,6 +72,7 @@ def incorrect_lti_is_rendered(_step):
|
||||
assert world.is_css_present('iframe', wait_time=2)
|
||||
assert not world.is_css_present('.link_lti_new_window', wait_time=0)
|
||||
assert not world.is_css_present('.error_message', wait_time=0)
|
||||
|
||||
#inside iframe test content is presented
|
||||
check_lti_iframe_content("Wrong LTI signature")
|
||||
|
||||
@@ -79,6 +86,7 @@ def set_correct_lti_passport(_step, user='Instructor'):
|
||||
world.lti_server.oauth_settings['client_secret']
|
||||
)]
|
||||
}
|
||||
|
||||
i_am_registered_for_the_course(coursenum, metadata, user)
|
||||
|
||||
|
||||
@@ -91,8 +99,10 @@ def set_incorrect_lti_passport(_step):
|
||||
"incorrect_lti_secret_key"
|
||||
)]
|
||||
}
|
||||
|
||||
i_am_registered_for_the_course(coursenum, metadata)
|
||||
|
||||
|
||||
@step('the course has an LTI component with (.*) fields(?:\:)?$') #, new_page is(.*), is_graded is(.*)
|
||||
def add_correct_lti_to_course(_step, fields):
|
||||
category = 'lti'
|
||||
@@ -100,6 +110,7 @@ def add_correct_lti_to_course(_step, fields):
|
||||
'lti_id': 'correct_lti_id',
|
||||
'launch_url': world.lti_server.oauth_settings['lti_base'] + world.lti_server.oauth_settings['lti_endpoint'],
|
||||
}
|
||||
|
||||
if fields.strip() == 'incorrect_lti_id': # incorrect fields
|
||||
metadata.update({
|
||||
'lti_id': 'incorrect_lti_id'
|
||||
@@ -132,7 +143,6 @@ def add_correct_lti_to_course(_step, fields):
|
||||
|
||||
|
||||
def create_course_for_lti(course, metadata):
|
||||
|
||||
# First clear the modulestore so we don't try to recreate
|
||||
# the same course twice
|
||||
# This also ensures that the necessary templates are loaded
|
||||
@@ -202,10 +212,12 @@ def i_am_registered_for_the_course(coursenum, metadata, user='Instructor'):
|
||||
user = BetaTesterFactory(course=course_location)
|
||||
normal_student = UserFactory()
|
||||
instructor = InstructorFactory(course=course_location)
|
||||
|
||||
assert not has_access(normal_student, course_descriptor, 'load')
|
||||
assert has_access(user, course_descriptor, 'load')
|
||||
assert has_access(instructor, course_descriptor, 'load')
|
||||
else:
|
||||
metadata.update({'start': datetime.datetime(1970, 1, 1, tzinfo=UTC)})
|
||||
create_course_for_lti(coursenum, metadata)
|
||||
course_descriptor = world.scenario_dict['COURSE']
|
||||
course_location = world.scenario_dict['COURSE'].location
|
||||
@@ -214,6 +226,7 @@ def i_am_registered_for_the_course(coursenum, metadata, user='Instructor'):
|
||||
# Enroll the user in the course and log them in
|
||||
if has_access(user, course_descriptor, 'load'):
|
||||
world.enroll_user(user, course_id(coursenum))
|
||||
|
||||
world.log_in(username=user.username, password='test')
|
||||
|
||||
|
||||
@@ -229,11 +242,11 @@ def check_lti_popup():
|
||||
url = world.browser.url
|
||||
basename = os.path.basename(url)
|
||||
pathname = os.path.splitext(basename)[0]
|
||||
|
||||
if pathname == u'correct_lti_endpoint':
|
||||
break
|
||||
|
||||
result = world.css_find('.result').first.text
|
||||
|
||||
assert result == u'This is LTI tool. Success.'
|
||||
|
||||
world.browser.driver.close() # Close the pop-up window
|
||||
@@ -279,3 +292,26 @@ def click_grade(_step):
|
||||
iframe.find_by_name('submit-button').first.click()
|
||||
assert iframe.is_text_present('LTI consumer (edX) responded with XML content')
|
||||
|
||||
|
||||
@step('I see in iframe that LTI role is (.*)$')
|
||||
def check_role(_step, role):
|
||||
world.is_css_present('iframe')
|
||||
location = world.scenario_dict['LTI'].location.html_id()
|
||||
iframe_name = 'ltiFrame-' + location
|
||||
with world.browser.get_iframe(iframe_name) as iframe:
|
||||
expected_role = 'Role: ' + role
|
||||
role = world.retry_on_exception(
|
||||
lambda: iframe.find_by_tag('h5').first.value,
|
||||
max_attempts=5,
|
||||
ignored_exceptions=ElementDoesNotExist
|
||||
)
|
||||
assert_equal(expected_role, role)
|
||||
|
||||
|
||||
@step('I switch to (.*)$')
|
||||
def switch_view(_step, view):
|
||||
staff_status = world.css_find('#staffstatus').first
|
||||
if staff_status.text != view:
|
||||
world.css_click('#staffstatus')
|
||||
world.wait_for_ajax_complete()
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@ class MockLTIRequestHandler(BaseHTTPRequestHandler):
|
||||
'''
|
||||
self._send_head(status_code)
|
||||
if getattr(self.server, 'grade_data', False): # lti can be graded
|
||||
url = "//{}:{}".format(self.server.server_host, self.server.server_port)
|
||||
url = "//%s:%s" % self.server.server_address
|
||||
response_str = textwrap.dedent("""
|
||||
<html>
|
||||
<head>
|
||||
@@ -196,13 +196,14 @@ class MockLTIRequestHandler(BaseHTTPRequestHandler):
|
||||
<h2>Graded IFrame loaded</h2>
|
||||
<h3>Server response is:</h3>
|
||||
<h3 class="result">{}</h3>
|
||||
<h5>Role: {role}</h5>
|
||||
</div>
|
||||
<form action="{url}/grade" method="post">
|
||||
<input type="submit" name="submit-button" value="Submit">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
""").format(message, url=url)
|
||||
""").format(message, role=self.post_dict['roles'], url=url)
|
||||
else: # lti can't be graded
|
||||
response_str = textwrap.dedent("""
|
||||
<html>
|
||||
@@ -214,6 +215,7 @@ class MockLTIRequestHandler(BaseHTTPRequestHandler):
|
||||
<h2>IFrame loaded</h2>
|
||||
<h3>Server response is:</h3>
|
||||
<h3 class="result">{}</h3>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -57,7 +57,7 @@ class MockLTIServerTest(unittest.TestCase):
|
||||
#wrong number of params and no signature
|
||||
payload = {
|
||||
'user_id': 'default_user_id',
|
||||
'role': 'student',
|
||||
'roles': 'Student',
|
||||
'oauth_nonce': '',
|
||||
'oauth_timestamp': '',
|
||||
}
|
||||
@@ -73,7 +73,7 @@ class MockLTIServerTest(unittest.TestCase):
|
||||
"""
|
||||
payload = {
|
||||
'user_id': 'default_user_id',
|
||||
'role': 'student',
|
||||
'roles': 'Student',
|
||||
'oauth_nonce': '',
|
||||
'oauth_timestamp': '',
|
||||
'oauth_consumer_key': 'test_client_key',
|
||||
@@ -100,7 +100,7 @@ class MockLTIServerTest(unittest.TestCase):
|
||||
"""
|
||||
payload = {
|
||||
'user_id': 'default_user_id',
|
||||
'role': 'student',
|
||||
'roles': 'Student',
|
||||
'oauth_nonce': '',
|
||||
'oauth_timestamp': '',
|
||||
'oauth_consumer_key': 'test_client_key',
|
||||
@@ -126,7 +126,7 @@ class MockLTIServerTest(unittest.TestCase):
|
||||
|
||||
payload = {
|
||||
'user_id': 'default_user_id',
|
||||
'role': 'student',
|
||||
'roles': 'Student',
|
||||
'oauth_nonce': '',
|
||||
'oauth_timestamp': '',
|
||||
'oauth_consumer_key': 'test_client_key',
|
||||
|
||||
@@ -17,7 +17,7 @@ import django.utils
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from capa.xqueue_interface import XQueueInterface
|
||||
from courseware.access import has_access
|
||||
from courseware.access import has_access, get_user_role
|
||||
from courseware.masquerade import setup_masquerade
|
||||
from courseware.model_data import FieldDataCache, DjangoKeyValueStore
|
||||
from lms.lib.xblock.field_data import LmsFieldData
|
||||
@@ -432,6 +432,7 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours
|
||||
# directly as the runtime i18n service.
|
||||
'i18n': django.utils.translation,
|
||||
},
|
||||
get_user_role=lambda: get_user_role(user, course_id),
|
||||
)
|
||||
|
||||
# pass position specified in URL to module through ModuleSystem
|
||||
|
||||
@@ -102,3 +102,48 @@ class AccessTestCase(TestCase):
|
||||
def test__user_passed_as_none(self):
|
||||
"""Ensure has_access handles a user being passed as null"""
|
||||
access.has_access(None, 'global', 'staff', None)
|
||||
|
||||
class UserRoleTestCase(TestCase):
|
||||
"""
|
||||
Tests for user roles.
|
||||
"""
|
||||
def setUp(self):
|
||||
self.course = Location('i4x://edX/toy/course/2012_Fall')
|
||||
self.anonymous_user = AnonymousUserFactory()
|
||||
self.student = UserFactory()
|
||||
self.global_staff = UserFactory(is_staff=True)
|
||||
self.course_staff = StaffFactory(course=self.course)
|
||||
self.course_instructor = InstructorFactory(course=self.course)
|
||||
|
||||
def test_user_role_staff(self):
|
||||
"""Ensure that user role is student for staff masqueraded as student."""
|
||||
self.assertEqual(
|
||||
'staff',
|
||||
access.get_user_role(self.course_staff, self.course.course_id)
|
||||
)
|
||||
# Masquerade staff
|
||||
self.course_staff.masquerade_as_student = True
|
||||
self.assertEqual(
|
||||
'student',
|
||||
access.get_user_role(self.course_staff, self.course.course_id)
|
||||
)
|
||||
|
||||
def test_user_role_instructor(self):
|
||||
"""Ensure that user role is student for instructor masqueraded as student."""
|
||||
self.assertEqual(
|
||||
'instructor',
|
||||
access.get_user_role(self.course_instructor, self.course.course_id)
|
||||
)
|
||||
# Masquerade instructor
|
||||
self.course_instructor.masquerade_as_student = True
|
||||
self.assertEqual(
|
||||
'student',
|
||||
access.get_user_role(self.course_instructor, self.course.course_id)
|
||||
)
|
||||
|
||||
def test_user_role_anonymous(self):
|
||||
"""Ensure that user role is student for anonymous user."""
|
||||
self.assertEqual(
|
||||
'student',
|
||||
access.get_user_role(self.anonymous_user, self.course.course_id)
|
||||
)
|
||||
|
||||
@@ -43,7 +43,7 @@ class TestLTI(BaseTestXmodule):
|
||||
u'launch_presentation_return_url': '',
|
||||
u'lti_message_type': u'basic-lti-launch-request',
|
||||
u'lti_version': 'LTI-1p0',
|
||||
u'role': u'student',
|
||||
u'roles': u'Student',
|
||||
|
||||
u'resource_link_id': module_id,
|
||||
u'lis_result_sourcedid': sourcedId,
|
||||
|
||||
Reference in New Issue
Block a user