Commit 0e5c81c0 by Xavier Antoviaque Committed by Jonathan Piacenti

xblock-external-ui: Add XBlock API call to render XBlock views

Included commits:
* xblock-external-ui: Include CSRF token in the API answer
* xblock-external-ui: Adds support for CORS headers (cross-domain request)
* xblock-external-ui: Include full path when building local_url
* xblock-external-ui: Fix TestHandleXBlockCallback & bok_choy, add tests
* xblock-external-ui: Only return `instance` in `_invoke_xblock_handler()`
* xblock-external-ui: Group resources by hash tag to avoid duplicate loads
* xblock-external-ui: Alternate referer check for CORS requests
* session-cookie-httponly: Allow to disable httponly on session cookies
* remove errant log message
* xblock-no-anonymous: Fail early if the XBlock view is called anonymously
parent 11e09cba
...@@ -43,6 +43,7 @@ CSRF cookie. ...@@ -43,6 +43,7 @@ CSRF cookie.
""" """
import logging import logging
import urlparse
from django.conf import settings from django.conf import settings
from django.middleware.csrf import CsrfViewMiddleware from django.middleware.csrf import CsrfViewMiddleware
......
...@@ -42,6 +42,8 @@ from lms.djangoapps.lms_xblock.runtime import LmsModuleSystem, unquote_slashes, ...@@ -42,6 +42,8 @@ from lms.djangoapps.lms_xblock.runtime import LmsModuleSystem, unquote_slashes,
from lms.djangoapps.lms_xblock.models import XBlockAsidesConfig from lms.djangoapps.lms_xblock.models import XBlockAsidesConfig
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import UsageKey, CourseKey from opaque_keys.edx.keys import UsageKey, CourseKey
from psychometrics.psychoanalyze import make_psychometrics_data_update_handler
from student.models import anonymous_id_for_user, user_by_anonymous_id
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.locations import SlashSeparatedCourseKey
from openedx.core.lib.xblock_utils import ( from openedx.core.lib.xblock_utils import (
replace_course_urls, replace_course_urls,
...@@ -943,6 +945,7 @@ def _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, course ...@@ -943,6 +945,7 @@ def _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, course
usage_id (str): A string of the form i4x://org/course/category/name@revision usage_id (str): A string of the form i4x://org/course/category/name@revision
handler (str): The name of the handler to invoke handler (str): The name of the handler to invoke
suffix (str): The suffix to pass to the handler when invoked suffix (str): The suffix to pass to the handler when invoked
user (User): The currently logged in user
""" """
# Check submitted files # Check submitted files
......
...@@ -19,7 +19,6 @@ from mock import MagicMock, patch, Mock ...@@ -19,7 +19,6 @@ from mock import MagicMock, patch, Mock
from opaque_keys.edx.keys import UsageKey, CourseKey from opaque_keys.edx.keys import UsageKey, CourseKey
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.locations import SlashSeparatedCourseKey
from pyquery import PyQuery from pyquery import PyQuery
from courseware.module_render import hash_resource
from xblock.field_data import FieldData from xblock.field_data import FieldData
from xblock.runtime import Runtime from xblock.runtime import Runtime
from xblock.fields import ScopeIds from xblock.fields import ScopeIds
...@@ -532,6 +531,28 @@ class TestHandleXBlockCallback(ModuleStoreTestCase, LoginEnrollmentTestCase): ...@@ -532,6 +531,28 @@ class TestHandleXBlockCallback(ModuleStoreTestCase, LoginEnrollmentTestCase):
'bad_dispatch', 'bad_dispatch',
) )
def test_xblock_view_handler(self):
args=[
'edX/toy/2012_Fall',
quote_slashes('i4x://edX/toy/videosequence/Toy_Videos'),
'student_view'
]
xblock_view_url = reverse(
'xblock_view',
args=args
)
request = self.request_factory.get(xblock_view_url)
request.user = self.mock_user
response = render.xblock_view(request, *args)
self.assertEquals(200, response.status_code)
expected = ['csrf_token', 'html', 'resources']
content = json.loads(response.content)
for section in expected:
self.assertIn(section, content)
self.assertIn('<div class="xblock xblock-student_view xmodule_display', content['html'])
@XBlock.register_temp_plugin(GradedStatelessXBlock, identifier='stateless_scorer') @XBlock.register_temp_plugin(GradedStatelessXBlock, identifier='stateless_scorer')
def test_score_without_student_state(self): def test_score_without_student_state(self):
course = CourseFactory.create() course = CourseFactory.create()
......
...@@ -331,6 +331,11 @@ FOOTER_BROWSER_CACHE_MAX_AGE = ENV_TOKENS.get('FOOTER_BROWSER_CACHE_MAX_AGE', FO ...@@ -331,6 +331,11 @@ FOOTER_BROWSER_CACHE_MAX_AGE = ENV_TOKENS.get('FOOTER_BROWSER_CACHE_MAX_AGE', FO
############# CORS headers for cross-domain requests ################# ############# CORS headers for cross-domain requests #################
if FEATURES.get('ENABLE_CORS_HEADERS') or FEATURES.get('ENABLE_CROSS_DOMAIN_CSRF_COOKIE'): if FEATURES.get('ENABLE_CORS_HEADERS') or FEATURES.get('ENABLE_CROSS_DOMAIN_CSRF_COOKIE'):
INSTALLED_APPS += ('corsheaders', 'cors_csrf')
MIDDLEWARE_CLASSES = (
'corsheaders.middleware.CorsMiddleware',
'cors_csrf.middleware.CorsCSRFMiddleware',
) + MIDDLEWARE_CLASSES
CORS_ALLOW_CREDENTIALS = True CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = ENV_TOKENS.get('CORS_ORIGIN_WHITELIST', ()) CORS_ORIGIN_WHITELIST = ENV_TOKENS.get('CORS_ORIGIN_WHITELIST', ())
CORS_ORIGIN_ALLOW_ALL = ENV_TOKENS.get('CORS_ORIGIN_ALLOW_ALL', False) CORS_ORIGIN_ALLOW_ALL = ENV_TOKENS.get('CORS_ORIGIN_ALLOW_ALL', False)
......
...@@ -144,6 +144,9 @@ FEATURES = { ...@@ -144,6 +144,9 @@ FEATURES = {
# with Shib. Feature was requested by Stanford's office of general counsel # with Shib. Feature was requested by Stanford's office of general counsel
'SHIB_DISABLE_TOS': False, 'SHIB_DISABLE_TOS': False,
# Allows to configure the LMS to provide CORS headers to serve requests from other domains
'ENABLE_CORS_HEADERS': False,
# Toggles OAuth2 authentication provider # Toggles OAuth2 authentication provider
'ENABLE_OAUTH2_PROVIDER': False, 'ENABLE_OAUTH2_PROVIDER': False,
...@@ -2074,18 +2077,23 @@ if FEATURES.get('AUTH_USE_CAS'): ...@@ -2074,18 +2077,23 @@ if FEATURES.get('AUTH_USE_CAS'):
INSTALLED_APPS += ('django_cas',) INSTALLED_APPS += ('django_cas',)
MIDDLEWARE_CLASSES += ('django_cas.middleware.CASMiddleware',) MIDDLEWARE_CLASSES += ('django_cas.middleware.CASMiddleware',)
############# Cross-domain requests #################
if FEATURES.get('ENABLE_CORS_HEADERS'):
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = ()
CORS_ORIGIN_ALLOW_ALL = False
# Default cache expiration for the cross-domain proxy HTML page. # Default cache expiration for the cross-domain proxy HTML page.
# This is a static page that can be iframed into an external page # This is a static page that can be iframed into an external page
# to simulate cross-domain requests. # to simulate cross-domain requests.
XDOMAIN_PROXY_CACHE_TIMEOUT = 60 * 15 XDOMAIN_PROXY_CACHE_TIMEOUT = 60 * 15
############# CORS headers for cross-domain requests #################
if FEATURES.get('ENABLE_CORS_HEADERS'):
CORS_ORIGIN_ALLOW_ALL = False
INSTALLED_APPS += ('corsheaders', 'cors_csrf')
MIDDLEWARE_CLASSES = (
'corsheaders.middleware.CorsMiddleware',
'cors_csrf.middleware.CorsCSRFMiddleware',
) + MIDDLEWARE_CLASSES
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = ()
###################### Registration ################################## ###################### Registration ##################################
# For each of the fields, give one of the following values: # For each of the fields, give one of the following values:
......
...@@ -264,8 +264,8 @@ if settings.COURSEWARE_ENABLED: ...@@ -264,8 +264,8 @@ if settings.COURSEWARE_ENABLED:
'courseware.module_render.handle_xblock_callback', 'courseware.module_render.handle_xblock_callback',
name='xblock_handler'), name='xblock_handler'),
url(r'^courses/{course_key}/xblock/{usage_key}/view/(?P<view_name>[^/]*)$'.format( url(r'^courses/{course_key}/xblock/{usage_key}/view/(?P<view_name>[^/]*)$'.format(
course_key=settings.COURSE_ID_PATTERN, course_key=settings.COURSE_ID_PATTERN,
usage_key=settings.USAGE_ID_PATTERN), usage_key=settings.USAGE_ID_PATTERN),
'courseware.module_render.xblock_view', 'courseware.module_render.xblock_view',
name='xblock_view'), name='xblock_view'),
url(r'^courses/{course_key}/xblock/{usage_key}/handler_noauth/(?P<handler>[^/]*)(?:/(?P<suffix>.*))?$'.format(course_key=settings.COURSE_ID_PATTERN, usage_key=settings.USAGE_ID_PATTERN), url(r'^courses/{course_key}/xblock/{usage_key}/handler_noauth/(?P<handler>[^/]*)(?:/(?P<suffix>.*))?$'.format(course_key=settings.COURSE_ID_PATTERN, usage_key=settings.USAGE_ID_PATTERN),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment