diff --git a/common/djangoapps/terrain/browser.py b/common/djangoapps/terrain/browser.py index 498197b590454cb978fb94db41a832657cd677cd..a554a34f196004b8a30f1be8e814cdf1ffaba698 100644 --- a/common/djangoapps/terrain/browser.py +++ b/common/djangoapps/terrain/browser.py @@ -12,7 +12,7 @@ from django.core.management import call_command from django.conf import settings from selenium.common.exceptions import WebDriverException from selenium.webdriver.common.desired_capabilities import DesiredCapabilities -from requests import put +import requests from base64 import encodestring from json import dumps @@ -54,12 +54,12 @@ def set_job_status(jobid, passed=True): """ Sets the job status on sauce labs """ - body_content = dumps({"passed": passed}) config = get_username_and_key() + url = 'http://saucelabs.com/rest/v1/{}/jobs/{}'.format(config['username'], world.jobid) + body_content = dumps({"passed": passed}) base64string = encodestring('{}:{}'.format(config['username'], config['access-key']))[:-1] - result = put('http://saucelabs.com/rest/v1/{}/jobs/{}'.format(config['username'], world.jobid), - data=body_content, - headers={"Authorization": "Basic {}".format(base64string)}) + headers = {"Authorization": "Basic {}".format(base64string)} + result = requests.put(url, data=body_content, headers=headers) return result.status_code == 200 diff --git a/common/lib/capa/capa/tests/__init__.py b/common/lib/capa/capa/tests/__init__.py index ac81ff66c4048429b9780a80735ffa43acd99d61..a1f75b0e929ce2145a1ddc9ac03562a1ebdeb4a0 100644 --- a/common/lib/capa/capa/tests/__init__.py +++ b/common/lib/capa/capa/tests/__init__.py @@ -36,6 +36,7 @@ def test_system(): user=Mock(), filestore=fs.osfs.OSFS(os.path.join(TEST_DIR, "test_files")), debug=True, + hostname="edx.org", xqueue={'interface': xqueue_interface, 'construct_callback': calledback_url, 'default_queuename': 'testqueue', 'waittime': 10}, node_path=os.environ.get("NODE_PATH", "/usr/local/lib/node_modules"), anonymous_student_id='student', diff --git a/common/lib/capa/capa/xqueue_interface.py b/common/lib/capa/capa/xqueue_interface.py index 4da8e11d532115fdd9ae820ba446d65d43f1eef0..81a221961dc3950da439c877146fde6d1491f656 100644 --- a/common/lib/capa/capa/xqueue_interface.py +++ b/common/lib/capa/capa/xqueue_interface.py @@ -64,7 +64,8 @@ class XQueueInterface(object): def __init__(self, url, django_auth, requests_auth=None): self.url = url self.auth = django_auth - self.session = requests.session(auth=requests_auth) + self.session = requests.Session() + self.session.auth = requests_auth def send_to_queue(self, header, body, files_to_upload=None): """ diff --git a/common/lib/sample-post.py b/common/lib/sample-post.py index a4985689bf1b5a7405ebae26c9fb1277703db718..d035d40709ee617d2e95134b26f60e88d5fd23da 100644 --- a/common/lib/sample-post.py +++ b/common/lib/sample-post.py @@ -39,7 +39,7 @@ username = prompt('username on server', 'victor@edx.org') password = prompt('password', 'abc123', safe=True) print "get csrf cookie" -session = requests.session() +session = requests.Session() r = session.get(server + '/') r.raise_for_status() diff --git a/common/lib/xmodule/xmodule/js/src/lti/lti.js b/common/lib/xmodule/xmodule/js/src/lti/lti.js index e5b6885e1bbf534a1005ed625b011ae78d35bfe6..7d5b183f216ba9e5139427dce8aa87a97316e02d 100644 --- a/common/lib/xmodule/xmodule/js/src/lti/lti.js +++ b/common/lib/xmodule/xmodule/js/src/lti/lti.js @@ -16,9 +16,9 @@ window.LTI = (function () { // If the Form's action attribute is set (i.e. we can perform a normal // submit), then we submit the form and make the frame shown. - if (form.attr('action')) { + if (form.attr('action') && form.attr('action') !== 'http://www.example.com') { form.submit(); - element.find('.lti').addClass('rendered') + element.find('.lti').addClass('rendered'); } } diff --git a/common/lib/xmodule/xmodule/lti_module.py b/common/lib/xmodule/xmodule/lti_module.py index bc07cea97ebb3759b59930c3f9fd988a149e88f9..a7ccb728d0a65dc925a379f4b21664511ae414f6 100644 --- a/common/lib/xmodule/xmodule/lti_module.py +++ b/common/lib/xmodule/xmodule/lti_module.py @@ -1,15 +1,12 @@ """ Module that allows to insert LTI tools to page. -Module uses current edx-platform 0.14.2 version of requests (oauth part). -Please update code when upgrading requests. - Protocol is oauth1, LTI version is 1.1.1: http://www.imsglobal.org/LTI/v1p1p1/ltiIMGv1p1p1.html """ import logging -import requests +import oauthlib.oauth1 import urllib from xmodule.editing_module import MetadataOnlyEditingDescriptor @@ -41,9 +38,12 @@ class LTIFields(object): vbid=put_book_id_here book_location=page/put_page_number_here + Default non-empty url for `launch_url` is needed due to oauthlib demand (url scheme should be presented):: + + https://github.com/idan/oauthlib/blob/master/oauthlib/oauth1/rfc5849/signature.py#L136 """ lti_id = String(help="Id of the tool", default='', scope=Scope.settings) - launch_url = String(help="URL of the tool", default='', scope=Scope.settings) + launch_url = String(help="URL of the tool", default='http://www.example.com', scope=Scope.settings) custom_parameters = List(help="Custom parameters (vbid, book_location, etc..)", scope=Scope.settings) @@ -192,7 +192,7 @@ class LTIModule(LTIFields, XModule): Also *anonymous student id* is passed to template and therefore to LTI provider. """ - client = requests.auth.Client( + client = oauthlib.oauth1.Client( client_key=unicode(client_key), client_secret=unicode(client_secret) ) @@ -215,14 +215,30 @@ class LTIModule(LTIFields, XModule): # appending custom parameter for signing body.update(custom_parameters) - # This is needed for body encoding: - headers = {'Content-Type': 'application/x-www-form-urlencoded'} + headers = { + # This is needed for body encoding: + 'Content-Type': 'application/x-www-form-urlencoded', + } + + try: + __, headers, __ = client.sign( + unicode(self.launch_url), + http_method=u'POST', + body=body, + headers=headers) + except ValueError: # scheme not in url + # Stubbing headers for now: + headers = { + u'Content-Type': u'application/x-www-form-urlencoded', + u'Authorization': u'oAuth ' # cont.. + u'oauth_nonce="80966668944732164491378916897", ' # cont.. + u'oauth_timestamp="1378916897", ' # cont.. + u'oauth_version="1.0", ' # cont.. + u'oauth_signature_method="HMAC-SHA1", ' # cont.. + u'oauth_consumer_key="", ' # cont.. + u'oauth_signature="frVp4JuvT1mVXlxktiAUjQ7%2F1cw%3D"', + } - __, headers, __ = client.sign( - unicode(self.launch_url), - http_method=u'POST', - body=body, - headers=headers) params = headers['Authorization'] # parse headers to pass to template as part of context: params = dict([param.strip().replace('"', '').split('=') for param in params.split(',')]) @@ -230,8 +246,8 @@ class LTIModule(LTIFields, XModule): params[u'oauth_nonce'] = params[u'OAuth oauth_nonce'] del params[u'OAuth oauth_nonce'] - # 0.14.2 (current) version of requests oauth library encodes signature, - # with 'Content-Type': 'application/x-www-form-urlencoded' + # oauthlib encodes signature with + # 'Content-Type': 'application/x-www-form-urlencoded' # so '='' becomes '%3D'. # We send form via browser, so browser will encode it again, # So we need to decode signature back: diff --git a/common/lib/xmodule/xmodule/open_ended_grading_classes/grading_service_module.py b/common/lib/xmodule/xmodule/open_ended_grading_classes/grading_service_module.py index 4c6a79a5f1ad5a2fb82b1b1b4a3fd85d969cbf44..6248c63091496f1fdde70bbc33b75f7d7270ed00 100644 --- a/common/lib/xmodule/xmodule/open_ended_grading_classes/grading_service_module.py +++ b/common/lib/xmodule/xmodule/open_ended_grading_classes/grading_service_module.py @@ -25,7 +25,7 @@ class GradingService(object): def __init__(self, config): self.username = config['username'] self.password = config['password'] - self.session = requests.session() + self.session = requests.Session() self.system = config['system'] def _login(self): @@ -42,7 +42,7 @@ class GradingService(object): response.raise_for_status() - return response.json + return response.json() def post(self, url, data, allow_redirects=False): """ @@ -88,9 +88,10 @@ class GradingService(object): Returns the result of operation(). Does not catch exceptions. """ response = operation() - if (response.json - and response.json.get('success') is False - and response.json.get('error') == 'login_required'): + resp_json = response.json() + if (resp_json + and resp_json.get('success') is False + and resp_json.get('error') == 'login_required'): # apparrently we aren't logged in. Try to fix that. r = self._login() if r and not r.get('success'): diff --git a/common/lib/xmodule/xmodule/tests/__init__.py b/common/lib/xmodule/xmodule/tests/__init__.py index b7e5ea8435c5f9a04299a2a8cf9230414d0bdd78..7f838aa242b2397c7ace5e18828236ff869a5615 100644 --- a/common/lib/xmodule/xmodule/tests/__init__.py +++ b/common/lib/xmodule/xmodule/tests/__init__.py @@ -62,6 +62,7 @@ def get_test_system(course_id=''): user=Mock(is_staff=False), filestore=Mock(), debug=True, + hostname="edx.org", xqueue={'interface': None, 'callback_url': '/', 'default_queuename': 'testqueue', 'waittime': 10, 'construct_callback' : Mock(side_effect="/")}, node_path=os.environ.get("NODE_PATH", "/usr/local/lib/node_modules"), xblock_field_data=lambda descriptor: descriptor._field_data, diff --git a/common/lib/xmodule/xmodule/x_module.py b/common/lib/xmodule/xmodule/x_module.py index 02feebea1b5b272ea8ee011de06410487483b877..edb9e7f64028968d87513723cfb3577b79e93d2a 100644 --- a/common/lib/xmodule/xmodule/x_module.py +++ b/common/lib/xmodule/xmodule/x_module.py @@ -833,7 +833,7 @@ class ModuleSystem(Runtime): def __init__( self, ajax_url, track_function, get_module, render_template, replace_urls, xblock_field_data, user=None, filestore=None, - debug=False, xqueue=None, publish=None, node_path="", + debug=False, hostname="", xqueue=None, publish=None, node_path="", anonymous_student_id='', course_id=None, open_ended_grading_interface=None, s3_interface=None, cache=None, can_execute_unsafe_code=None, replace_course_urls=None, @@ -897,6 +897,7 @@ class ModuleSystem(Runtime): self.get_module = get_module self.render_template = render_template self.DEBUG = self.debug = debug + self.HOSTNAME = self.hostname = hostname self.seed = user.id if user is not None else 0 self.replace_urls = replace_urls self.node_path = node_path diff --git a/docs/shared/requirements.txt b/docs/shared/requirements.txt index 7dc7da8a75b9476e3fd51cea884a83eef796acba..74c05d6d53f8e6f63b43c73065c863cecd61318d 100644 --- a/docs/shared/requirements.txt +++ b/docs/shared/requirements.txt @@ -36,6 +36,7 @@ mako==0.7.3 Markdown==2.2.1 networkx==1.7 nltk==2.0.4 +oauthlib==0.5.1 paramiko==1.9.0 path.py==3.0.1 Pillow==1.7.8 @@ -48,7 +49,7 @@ python-memcached==1.48 python-openid==2.2.5 pytz==2012h PyYAML==3.10 -requests==0.14.2 +requests==1.2.3 Shapely==1.2.16 sorl-thumbnail==11.12 South==0.7.6 diff --git a/lms/djangoapps/courseware/mock_lti_server/mock_lti_server.py b/lms/djangoapps/courseware/mock_lti_server/mock_lti_server.py index ba9cea84d67a4ca03488f2569a864d552b41df1a..833df3a1c3980e093b74ee237955e1ad01e631a5 100644 --- a/lms/djangoapps/courseware/mock_lti_server/mock_lti_server.py +++ b/lms/djangoapps/courseware/mock_lti_server/mock_lti_server.py @@ -1,6 +1,6 @@ from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler import urlparse -from requests.packages.oauthlib.oauth1.rfc5849 import signature +from oauthlib.oauth1.rfc5849 import signature import mock from logging import getLogger logger = getLogger(__name__) diff --git a/lms/djangoapps/courseware/mock_youtube_server/mock_youtube_server.py b/lms/djangoapps/courseware/mock_youtube_server/mock_youtube_server.py index 46b269dda68348e5bfadcf6c2702811106410054..16ab36b6f799f726d4ba72d50f8e203c8b915ddd 100644 --- a/lms/djangoapps/courseware/mock_youtube_server/mock_youtube_server.py +++ b/lms/djangoapps/courseware/mock_youtube_server/mock_youtube_server.py @@ -1,6 +1,5 @@ from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler import urlparse -from requests.packages.oauthlib.oauth1.rfc5849 import signature import mock import threading import json diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 53f9c57f38a68ae8a0f05e06cf6391833b59e6f2..b6d22fbf57af3d4f922dd726ba58ca8c3a25eb1a 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -347,6 +347,8 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours filestore=descriptor.system.resources_fs, get_module=inner_get_module, user=user, + debug=settings.DEBUG, + hostname=settings.SITE_NAME, # TODO (cpennington): This should be removed when all html from # a module is coming through get_html and is therefore covered # by the replace_static_urls code below @@ -380,7 +382,6 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours # pass position specified in URL to module through ModuleSystem system.set('position', position) - system.set('DEBUG', settings.DEBUG) if settings.MITX_FEATURES.get('ENABLE_PSYCHOMETRICS'): system.set( 'psychometrics_handler', # set callback for updating PsychometricsData diff --git a/lms/djangoapps/courseware/tests/test_lti.py b/lms/djangoapps/courseware/tests/test_lti.py index d2b4ea686700438802e075663583b4fce595c374..596ec2dd9a19ab3013395d62827d8c979c582891 100644 --- a/lms/djangoapps/courseware/tests/test_lti.py +++ b/lms/djangoapps/courseware/tests/test_lti.py @@ -1,6 +1,6 @@ """LTI integration tests""" -import requests +import oauthlib from . import BaseTestXmodule from collections import OrderedDict import mock @@ -11,7 +11,8 @@ class TestLTI(BaseTestXmodule): Integration test for lti xmodule. It checks overall code, by assuring that context that goes to template is correct. - As part of that, checks oauth signature generation by mocking signing function of `requests` library. + As part of that, checks oauth signature generation by mocking signing function + of `oauthlib` library. """ CATEGORY = "lti" @@ -43,7 +44,7 @@ class TestLTI(BaseTestXmodule): u'oauth_signature': mocked_decoded_signature } - saved_sign = requests.auth.Client.sign + saved_sign = oauthlib.oauth1.Client.sign def mocked_sign(self, *args, **kwargs): """ @@ -60,7 +61,7 @@ class TestLTI(BaseTestXmodule): headers[u'Authorization'] = ', '.join([k+'="'+v+'"' for k, v in old_parsed.items()]) return None, headers, None - patcher = mock.patch.object(requests.auth.Client, "sign", mocked_sign) + patcher = mock.patch.object(oauthlib.oauth1.Client, "sign", mocked_sign) patcher.start() self.addCleanup(patcher.stop) @@ -74,6 +75,6 @@ class TestLTI(BaseTestXmodule): 'input_fields': self.correct_headers, 'element_class': self.item_module.location.category, 'element_id': self.item_module.location.html_id(), - 'launch_url': '', # default value + 'launch_url': 'http://www.example.com', # default value } self.assertDictEqual(generated_context, expected_context) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index 7d55b001d02aad795ce87684c59e701ab4a2619a..cd0b230423cba2ef341402804fdf0f8bcc7298f0 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -4,6 +4,7 @@ Unit tests for instructor.api methods. # pylint: disable=E1111 import unittest import json +import requests from urllib import quote from django.conf import settings from django.test import TestCase @@ -756,7 +757,7 @@ class TestInstructorAPIAnalyticsProxy(ModuleStoreTestCase, LoginEnrollmentTestCa class FakeProxyResponse(object): """ Fake successful requests response object. """ def __init__(self): - self.status_code = instructor.views.api.codes.OK + self.status_code = requests.status_codes.codes.OK self.content = '{"test_content": "robot test content"}' class FakeBadProxyResponse(object): diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index e5498585a3e59944731d0686925bd53eee17f9f0..2fcc953e527c8b37a98dca276c179a7a3cc91685 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -9,7 +9,6 @@ Many of these GETs may become PUTs in the future. import re import logging import requests -from requests.status_codes import codes from collections import OrderedDict from django.conf import settings from django_future.csrf import ensure_csrf_cookie diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 77fbe40ecc41c9d672f37ab78ad2df80e92306b2..44ab122c94fd340a6760f929a04dab9d91914991 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -39,6 +39,7 @@ mako==0.7.3 Markdown==2.2.1 networkx==1.7 nltk==2.0.4 +oauthlib==0.5.1 paramiko==1.9.0 path.py==3.0.1 Pillow==1.7.8 @@ -53,7 +54,7 @@ python-memcached==1.48 python-openid==2.2.5 pytz==2012h PyYAML==3.10 -requests==0.14.2 +requests==1.2.3 scipy==0.11.0 Shapely==1.2.16 sorl-thumbnail==11.12