Merge branch 'release' into 'master' after successful rc/2014-09-17 deploy.
This commit is contained in:
@@ -644,6 +644,9 @@ def _get_module_info(xblock, rewrite_static_links=True):
|
||||
course_id=xblock.location.course_key
|
||||
)
|
||||
|
||||
# Pre-cache has changes for the entire course because we'll need it for the ancestor info
|
||||
modulestore().has_changes(modulestore().get_course(xblock.location.course_key, depth=None))
|
||||
|
||||
# Note that children aren't being returned until we have a use case.
|
||||
return create_xblock_info(xblock, data=data, metadata=own_metadata(xblock), include_ancestor_info=True)
|
||||
|
||||
|
||||
@@ -100,9 +100,9 @@ class GetItemTest(ItemTest):
|
||||
return html, resources
|
||||
|
||||
@ddt.data(
|
||||
(1, 14, 16, 30, 30),
|
||||
(2, 15, 17, 39, 32),
|
||||
(3, 16, 18, 52, 34),
|
||||
(1, 21, 23, 35, 37),
|
||||
(2, 22, 24, 38, 39),
|
||||
(3, 23, 25, 41, 41),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_get_query_count(self, branching_factor, chapter_queries, section_queries, unit_queries, problem_queries):
|
||||
|
||||
@@ -103,6 +103,16 @@ def open_source_footer_context_processor(request):
|
||||
)
|
||||
|
||||
|
||||
def microsite_footer_context_processor(request):
|
||||
"""
|
||||
Checks the site name to determine whether to use the edX.org footer or the Open Source Footer.
|
||||
"""
|
||||
return dict(
|
||||
[
|
||||
("IS_REQUEST_IN_MICROSITE", microsite.is_request_in_microsite())
|
||||
]
|
||||
)
|
||||
|
||||
def render_to_string(template_name, dictionary, context=None, namespace='main'):
|
||||
|
||||
# see if there is an override template defined in the microsite
|
||||
|
||||
@@ -108,12 +108,15 @@ class ImageAnnotationModule(AnnotatableFields, XModule):
|
||||
|
||||
self.instructions = self._extract_instructions(xmltree)
|
||||
self.openseadragonjson = html_to_text(etree.tostring(xmltree.find('json'), encoding='unicode'))
|
||||
self.user = ""
|
||||
self.user_email = ""
|
||||
self.is_course_staff = False
|
||||
if self.runtime.get_user_role() in ['instructor', 'staff']:
|
||||
self.is_course_staff = True
|
||||
if self.runtime.get_real_user is not None:
|
||||
self.user = self.runtime.get_real_user(self.runtime.anonymous_student_id).email
|
||||
try:
|
||||
self.user_email = self.runtime.get_real_user(self.runtime.anonymous_student_id).email
|
||||
except: # pylint: disable=broad-except
|
||||
self.user_email = _("No email address found.")
|
||||
|
||||
def _extract_instructions(self, xmltree):
|
||||
""" Removes <instructions> from the xmltree and returns them as a string, otherwise None. """
|
||||
@@ -124,7 +127,7 @@ class ImageAnnotationModule(AnnotatableFields, XModule):
|
||||
context = {
|
||||
'display_name': self.display_name_with_default,
|
||||
'instructions_html': self.instructions,
|
||||
'token': retrieve_token(self.user, self.annotation_token_secret),
|
||||
'token': retrieve_token(self.user_email, self.annotation_token_secret),
|
||||
'tag': self.instructor_tags,
|
||||
'openseadragonjson': self.openseadragonjson,
|
||||
'annotation_storage': self.annotation_storage_url,
|
||||
|
||||
@@ -597,6 +597,7 @@ class DraftModuleStore(MongoModuleStore):
|
||||
bulk_record.dirty = True
|
||||
self.collection.remove({'_id': {'$in': to_be_deleted}}, safe=self.collection.safe)
|
||||
|
||||
@MongoModuleStore.memoize_request_cache
|
||||
def has_changes(self, xblock):
|
||||
"""
|
||||
Check if the subtree rooted at xblock has any drafts and thus may possibly have changes
|
||||
|
||||
@@ -107,7 +107,10 @@ class TextAnnotationModule(AnnotatableFields, XModule):
|
||||
if self.runtime.get_user_role() in ['instructor', 'staff']:
|
||||
self.is_course_staff = True
|
||||
if self.runtime.get_real_user is not None:
|
||||
self.user_email = self.runtime.get_real_user(self.runtime.anonymous_student_id).email
|
||||
try:
|
||||
self.user_email = self.runtime.get_real_user(self.runtime.anonymous_student_id).email
|
||||
except: # pylint: disable=broad-except
|
||||
self.user_email = _("No email address found.")
|
||||
|
||||
def _extract_instructions(self, xmltree):
|
||||
""" Removes <instructions> from the xmltree and returns them as a string, otherwise None. """
|
||||
|
||||
@@ -107,7 +107,10 @@ class VideoAnnotationModule(AnnotatableFields, XModule):
|
||||
if self.runtime.get_user_role() in ['instructor', 'staff']:
|
||||
self.is_course_staff = True
|
||||
if self.runtime.get_real_user is not None:
|
||||
self.user_email = self.runtime.get_real_user(self.runtime.anonymous_student_id).email
|
||||
try:
|
||||
self.user_email = self.runtime.get_real_user(self.runtime.anonymous_student_id).email
|
||||
except: # pylint: disable=broad-except
|
||||
self.user_email = _("No email address found.")
|
||||
|
||||
def _extract_instructions(self, xmltree):
|
||||
""" Removes <instructions> from the xmltree and returns them as a string, otherwise None. """
|
||||
|
||||
@@ -5,12 +5,13 @@ Tests for the certificates models.
|
||||
from django.test import TestCase
|
||||
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
|
||||
from student.tests.factories import UserFactory
|
||||
from certificates.models import CertificateStatuses, GeneratedCertificate, certificate_status_for_student
|
||||
|
||||
|
||||
class CertificatesModelTest(TestCase):
|
||||
class CertificatesModelTest(ModuleStoreTestCase):
|
||||
"""
|
||||
Tests for the GeneratedCertificate model
|
||||
"""
|
||||
|
||||
@@ -15,6 +15,7 @@ from instructor_analytics.basic import (
|
||||
)
|
||||
from courseware.tests.factories import InstructorFactory
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
|
||||
|
||||
class TestAnalyticsBasic(TestCase):
|
||||
@@ -52,7 +53,7 @@ class TestAnalyticsBasic(TestCase):
|
||||
self.assertEqual(set(AVAILABLE_FEATURES), set(STUDENT_FEATURES + PROFILE_FEATURES))
|
||||
|
||||
|
||||
class TestCourseSaleRecordsAnalyticsBasic(TestCase):
|
||||
class TestCourseSaleRecordsAnalyticsBasic(ModuleStoreTestCase):
|
||||
""" Test basic course sale records analytics functions. """
|
||||
def setUp(self):
|
||||
"""
|
||||
@@ -100,7 +101,7 @@ class TestCourseSaleRecordsAnalyticsBasic(TestCase):
|
||||
self.assertEqual(sale_record['total_codes'], 5)
|
||||
|
||||
|
||||
class TestCourseRegistrationCodeAnalyticsBasic(TestCase):
|
||||
class TestCourseRegistrationCodeAnalyticsBasic(ModuleStoreTestCase):
|
||||
""" Test basic course registration codes analytics functions. """
|
||||
def setUp(self):
|
||||
"""
|
||||
|
||||
@@ -20,11 +20,6 @@ from util.testing import UrlResetMixin
|
||||
class NotificationPrefViewTest(UrlResetMixin, TestCase):
|
||||
INITIALIZATION_VECTOR = "\x00" * 16
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
# Make sure global state is set up appropriately
|
||||
Client().get("/")
|
||||
|
||||
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
|
||||
def setUp(self):
|
||||
super(NotificationPrefViewTest, self).setUp()
|
||||
|
||||
1
lms/djangoapps/oauth2_handler/__init__.py
Normal file
1
lms/djangoapps/oauth2_handler/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from oauth2_handler.handlers import IDTokenHandler, UserInfoHandler
|
||||
174
lms/djangoapps/oauth2_handler/handlers.py
Normal file
174
lms/djangoapps/oauth2_handler/handlers.py
Normal file
@@ -0,0 +1,174 @@
|
||||
""" Handlers for OpenID Connect provider. """
|
||||
|
||||
import branding
|
||||
from courseware.access import has_access
|
||||
from student.models import anonymous_id_for_user
|
||||
from user_api.models import UserPreference
|
||||
from lang_pref import LANGUAGE_KEY
|
||||
|
||||
|
||||
class OpenIDHandler(object):
|
||||
""" Basic OpenID Connect scope handler. """
|
||||
|
||||
def scope_openid(self, _data):
|
||||
""" Only override the sub (subject) claim. """
|
||||
return ['sub']
|
||||
|
||||
def claim_sub(self, data):
|
||||
"""
|
||||
Return the value of the sub (subject) claim. The value should be
|
||||
unique for each user.
|
||||
|
||||
"""
|
||||
|
||||
# Use the anonymous ID without any course as unique identifier.
|
||||
# Note that this ID is derived using the value of the `SECRET_KEY`
|
||||
# setting, this means that users will have different sub
|
||||
# values for different deployments.
|
||||
value = anonymous_id_for_user(data['user'], None)
|
||||
return value
|
||||
|
||||
|
||||
class ProfileHandler(object):
|
||||
""" Basic OpenID Connect `profile` scope handler with `locale` claim. """
|
||||
|
||||
def scope_profile(self, _data):
|
||||
""" Add the locale claim. """
|
||||
return ['locale']
|
||||
|
||||
def claim_locale(self, data):
|
||||
"""
|
||||
Return the locale for the users based on their preferences.
|
||||
Does not return a value if the users have not set their locale preferences.
|
||||
|
||||
"""
|
||||
|
||||
language = UserPreference.get_preference(data['user'], LANGUAGE_KEY)
|
||||
return language
|
||||
|
||||
|
||||
class CourseAccessHandler(object):
|
||||
"""
|
||||
Defines two new scopes: `course_instructor` and `course_staff`. Each one is
|
||||
valid only if the user is instructor or staff of at least one course.
|
||||
|
||||
Each new scope has a corresponding claim: `instructor_courses` and
|
||||
`staff_courses` that lists the course_ids for which the user as instructor
|
||||
or staff privileges.
|
||||
|
||||
The claims support claim request values. In other words, if no claim is
|
||||
requested it returns all the courses for the corresponding privileges. If a
|
||||
claim request is used, then it only returns the from the list of requested
|
||||
values that have the corresponding privileges.
|
||||
|
||||
For example, if the user is staff of course_a and course_b but not
|
||||
course_c, the request:
|
||||
|
||||
scope = openid course_staff
|
||||
|
||||
will return:
|
||||
|
||||
{staff_courses: [course_a, course_b] }
|
||||
|
||||
If the request is:
|
||||
|
||||
claims = {userinfo: {staff_courses=[course_b, course_d]}}
|
||||
|
||||
the result will be:
|
||||
|
||||
{staff_courses: [course_b] }.
|
||||
|
||||
This is useful to quickly determine if a user has the right
|
||||
privileges for a given course.
|
||||
|
||||
For a description of the function naming and arguments, see:
|
||||
|
||||
`oauth2_provider/oidc/handlers.py`
|
||||
|
||||
"""
|
||||
|
||||
def scope_course_instructor(self, data):
|
||||
"""
|
||||
Scope `course_instructor` valid only if the user is an instructor
|
||||
of at least one course.
|
||||
|
||||
"""
|
||||
|
||||
course_ids = self._courses_with_access_type(data, 'instructor')
|
||||
return ['instructor_courses'] if course_ids else None
|
||||
|
||||
def scope_course_staff(self, data):
|
||||
"""
|
||||
Scope `course_staff` valid only if the user is an instructor of at
|
||||
least one course.
|
||||
|
||||
"""
|
||||
|
||||
course_ids = self._courses_with_access_type(data, 'staff')
|
||||
return ['staff_courses'] if course_ids else None
|
||||
|
||||
def claim_instructor_courses(self, data):
|
||||
"""
|
||||
Claim `instructor_courses` with list of course_ids for which the
|
||||
user has instructor privileges.
|
||||
|
||||
"""
|
||||
return self._courses_with_access_type(data, 'instructor')
|
||||
|
||||
def claim_staff_courses(self, data):
|
||||
"""
|
||||
Claim `staff_courses` with list of course_ids for which the user
|
||||
has staff privileges.
|
||||
|
||||
"""
|
||||
return self._courses_with_access_type(data, 'staff')
|
||||
|
||||
def _courses_with_access_type(self, data, access_type):
|
||||
"""
|
||||
Utility function to list all courses for a user according to the
|
||||
access type.
|
||||
|
||||
The field `data` follows the handler specification in:
|
||||
|
||||
`oauth2_provider/oidc/handlers.py`
|
||||
|
||||
"""
|
||||
|
||||
user = data['user']
|
||||
values = set(data.get('values', []))
|
||||
|
||||
courses = branding.get_visible_courses()
|
||||
courses = (c for c in courses if has_access(user, access_type, c))
|
||||
course_ids = (unicode(c.id) for c in courses)
|
||||
|
||||
# If values was provided, return only the requested authorized courses
|
||||
if values:
|
||||
return [c for c in course_ids if c in values]
|
||||
else:
|
||||
return [c for c in course_ids]
|
||||
|
||||
|
||||
class IDTokenHandler(OpenIDHandler, ProfileHandler, CourseAccessHandler):
|
||||
"""
|
||||
Configure the ID Token handler for the LMS.
|
||||
|
||||
Note that the values of the claims `instructor_courses` and
|
||||
`staff_courses` are not included in the ID Token. The rationale is
|
||||
that for global staff, the list of courses returned could be very
|
||||
large. Instead they could check for specific courses using the
|
||||
UserInfo endpoint.
|
||||
|
||||
"""
|
||||
|
||||
def claim_instructor_courses(self, data):
|
||||
# Don't return list of courses in ID Tokens
|
||||
return None
|
||||
|
||||
def claim_staff_courses(self, data):
|
||||
# Don't return list of courses in ID Tokens
|
||||
return None
|
||||
|
||||
|
||||
class UserInfoHandler(OpenIDHandler, ProfileHandler, CourseAccessHandler):
|
||||
""" Configure the UserInfo handler for the LMS. """
|
||||
pass
|
||||
148
lms/djangoapps/oauth2_handler/tests.py
Normal file
148
lms/djangoapps/oauth2_handler/tests.py
Normal file
@@ -0,0 +1,148 @@
|
||||
# pylint: disable=missing-docstring
|
||||
from django.test.utils import override_settings
|
||||
from django.test import TestCase
|
||||
|
||||
from courseware.tests.tests import TEST_DATA_MIXED_MODULESTORE
|
||||
from lang_pref import LANGUAGE_KEY
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from student.models import anonymous_id_for_user
|
||||
from student.roles import CourseStaffRole, CourseInstructorRole
|
||||
from student.tests.factories import UserFactory, UserProfileFactory
|
||||
from user_api.models import UserPreference
|
||||
|
||||
# Will also run default tests for IDTokens and UserInfo
|
||||
from oauth2_provider.tests import IDTokenTestCase, UserInfoTestCase
|
||||
|
||||
|
||||
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
|
||||
class BaseTestMixin(TestCase):
|
||||
profile = None
|
||||
|
||||
def setUp(self):
|
||||
super(BaseTestMixin, self).setUp()
|
||||
|
||||
self.course_key = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
|
||||
self.course_id = unicode(self.course_key)
|
||||
|
||||
self.user_factory = UserFactory
|
||||
self.set_user(self.make_user())
|
||||
|
||||
def set_user(self, user):
|
||||
super(BaseTestMixin, self).set_user(user)
|
||||
self.profile = UserProfileFactory(user=self.user)
|
||||
|
||||
|
||||
class IDTokenTest(BaseTestMixin, IDTokenTestCase):
|
||||
def test_sub_claim(self):
|
||||
scopes, claims = self.get_new_id_token_values('openid')
|
||||
self.assertIn('openid', scopes)
|
||||
|
||||
sub = claims['sub']
|
||||
|
||||
expected_sub = anonymous_id_for_user(self.user, None)
|
||||
self.assertEqual(sub, expected_sub)
|
||||
|
||||
def test_user_without_locale_claim(self):
|
||||
scopes, claims = self.get_new_id_token_values('openid profile')
|
||||
self.assertIn('profile', scopes)
|
||||
self.assertNotIn('locale', claims)
|
||||
|
||||
def test_user_wit_locale_claim(self):
|
||||
language = 'en'
|
||||
UserPreference.set_preference(self.user, LANGUAGE_KEY, language)
|
||||
scopes, claims = self.get_new_id_token_values('openid profile')
|
||||
|
||||
self.assertIn('profile', scopes)
|
||||
|
||||
locale = claims['locale']
|
||||
self.assertEqual(language, locale)
|
||||
|
||||
def test_no_special_course_access(self):
|
||||
scopes, claims = self.get_new_id_token_values('openid course_instructor course_staff')
|
||||
|
||||
self.assertNotIn('course_staff', scopes)
|
||||
self.assertNotIn('staff_courses', claims)
|
||||
|
||||
self.assertNotIn('course_instructor', scopes)
|
||||
self.assertNotIn('instructor_courses', claims)
|
||||
|
||||
def test_course_staff_courses(self):
|
||||
CourseStaffRole(self.course_key).add_users(self.user)
|
||||
|
||||
scopes, claims = self.get_new_id_token_values('openid course_staff')
|
||||
|
||||
self.assertIn('course_staff', scopes)
|
||||
self.assertNotIn('staff_courses', claims) # should not return courses in id_token
|
||||
|
||||
def test_course_instructor_courses(self):
|
||||
CourseInstructorRole(self.course_key).add_users(self.user)
|
||||
|
||||
scopes, claims = self.get_new_id_token_values('openid course_instructor')
|
||||
|
||||
self.assertIn('course_instructor', scopes)
|
||||
self.assertNotIn('instructor_courses', claims) # should not return courses in id_token
|
||||
|
||||
|
||||
class UserInfoTest(BaseTestMixin, UserInfoTestCase):
|
||||
def token_for_scope(self, scope):
|
||||
full_scope = 'openid %s' % scope
|
||||
self.set_access_token_scope(full_scope)
|
||||
|
||||
token = self.access_token.token # pylint: disable=no-member
|
||||
return full_scope, token
|
||||
|
||||
def get_with_scope(self, scope):
|
||||
scope, token = self.token_for_scope(scope)
|
||||
result, claims = self.get_userinfo(token, scope)
|
||||
self.assertEqual(result.status_code, 200)
|
||||
|
||||
return claims
|
||||
|
||||
def get_with_claim_value(self, scope, claim, values):
|
||||
_full_scope, token = self.token_for_scope(scope)
|
||||
|
||||
result, claims = self.get_userinfo(
|
||||
token,
|
||||
claims={claim: {'values': values}}
|
||||
)
|
||||
|
||||
self.assertEqual(result.status_code, 200)
|
||||
return claims
|
||||
|
||||
def test_request_staff_courses_using_scope(self):
|
||||
CourseStaffRole(self.course_key).add_users(self.user)
|
||||
claims = self.get_with_scope('course_staff')
|
||||
|
||||
courses = claims['staff_courses']
|
||||
self.assertIn(self.course_id, courses)
|
||||
self.assertEqual(len(courses), 1)
|
||||
|
||||
def test_request_instructor_courses_using_scope(self):
|
||||
CourseInstructorRole(self.course_key).add_users(self.user)
|
||||
claims = self.get_with_scope('course_instructor')
|
||||
|
||||
courses = claims['instructor_courses']
|
||||
self.assertIn(self.course_id, courses)
|
||||
self.assertEqual(len(courses), 1)
|
||||
|
||||
def test_request_staff_courses_with_claims(self):
|
||||
CourseStaffRole(self.course_key).add_users(self.user)
|
||||
|
||||
values = [self.course_id, 'some_invalid_course']
|
||||
claims = self.get_with_claim_value('course_staff', 'staff_courses', values)
|
||||
self.assertEqual(len(claims), 2)
|
||||
|
||||
courses = claims['staff_courses']
|
||||
self.assertIn(self.course_id, courses)
|
||||
self.assertEqual(len(courses), 1)
|
||||
|
||||
def test_request_instructor_courses_with_claims(self):
|
||||
CourseInstructorRole(self.course_key).add_users(self.user)
|
||||
|
||||
values = ['edX/toy/TT_2012_Fall', self.course_id, 'invalid_course_id']
|
||||
claims = self.get_with_claim_value('course_instructor', 'instructor_courses', values)
|
||||
self.assertEqual(len(claims), 2)
|
||||
|
||||
courses = claims['instructor_courses']
|
||||
self.assertIn(self.course_id, courses)
|
||||
self.assertEqual(len(courses), 1)
|
||||
@@ -443,10 +443,13 @@ TIME_ZONE_DISPLAYED_FOR_DEADLINES = ENV_TOKENS.get("TIME_ZONE_DISPLAYED_FOR_DEAD
|
||||
##### X-Frame-Options response header settings #####
|
||||
X_FRAME_OPTIONS = ENV_TOKENS.get('X_FRAME_OPTIONS', X_FRAME_OPTIONS)
|
||||
|
||||
|
||||
##### Third-party auth options ################################################
|
||||
THIRD_PARTY_AUTH = AUTH_TOKENS.get('THIRD_PARTY_AUTH', THIRD_PARTY_AUTH)
|
||||
|
||||
##### OAUTH2 Provider ##############
|
||||
if FEATURES.get('ENABLE_OAUTH2_PROVIDER'):
|
||||
OAUTH_OIDC_ISSUER = ENV_TOKENS['OAUTH_OIDC_ISSUER']
|
||||
|
||||
##### ADVANCED_SECURITY_CONFIG #####
|
||||
ADVANCED_SECURITY_CONFIG = ENV_TOKENS.get('ADVANCED_SECURITY_CONFIG', {})
|
||||
|
||||
|
||||
@@ -123,6 +123,9 @@ FEATURES = {
|
||||
# with Shib. Feature was requested by Stanford's office of general counsel
|
||||
'SHIB_DISABLE_TOS': False,
|
||||
|
||||
# Toggles OAuth2 authentication provider
|
||||
'ENABLE_OAUTH2_PROVIDER': False,
|
||||
|
||||
# Can be turned off if course lists need to be hidden. Effects views and templates.
|
||||
'COURSES_ARE_BROWSABLE': True,
|
||||
|
||||
@@ -335,6 +338,28 @@ STATUS_MESSAGE_PATH = ENV_ROOT / "status_message.json"
|
||||
############################ OpenID Provider ##################################
|
||||
OPENID_PROVIDER_TRUSTED_ROOTS = ['cs50.net', '*.cs50.net']
|
||||
|
||||
############################ OAUTH2 Provider ###################################
|
||||
|
||||
# OpenID Connect issuer ID. Normally the URL of the authentication endpoint.
|
||||
|
||||
OAUTH_OIDC_ISSUER = 'https:/example.com/oauth2'
|
||||
|
||||
# OpenID Connect claim handlers
|
||||
|
||||
OAUTH_OIDC_ID_TOKEN_HANDLERS = (
|
||||
'oauth2_provider.oidc.handlers.BasicIDTokenHandler',
|
||||
'oauth2_provider.oidc.handlers.ProfileHandler',
|
||||
'oauth2_provider.oidc.handlers.EmailHandler',
|
||||
'oauth2_handler.IDTokenHandler'
|
||||
)
|
||||
|
||||
OAUTH_OIDC_USERINFO_HANDLERS = (
|
||||
'oauth2_provider.oidc.handlers.BasicUserInfoHandler',
|
||||
'oauth2_provider.oidc.handlers.ProfileHandler',
|
||||
'oauth2_provider.oidc.handlers.EmailHandler',
|
||||
'oauth2_handler.UserInfoHandler'
|
||||
)
|
||||
|
||||
################################## EDX WEB #####################################
|
||||
# This is where we stick our compiled template files. Most of the app uses Mako
|
||||
# templates
|
||||
@@ -380,6 +405,9 @@ TEMPLATE_CONTEXT_PROCESSORS = (
|
||||
|
||||
# Shoppingcart processor (detects if request.user has a cart)
|
||||
'shoppingcart.context_processor.user_has_cart_context_processor',
|
||||
|
||||
# Allows the open edX footer to be leveraged in Django Templates.
|
||||
'edxmako.shortcuts.microsite_footer_context_processor',
|
||||
)
|
||||
|
||||
# use the ratelimit backend to prevent brute force attacks
|
||||
@@ -1311,6 +1339,11 @@ INSTALLED_APPS = (
|
||||
'external_auth',
|
||||
'django_openid_auth',
|
||||
|
||||
# OAuth2 Provider
|
||||
'provider',
|
||||
'provider.oauth2',
|
||||
'oauth2_provider',
|
||||
|
||||
# For the wiki
|
||||
'wiki', # The new django-wiki from benjaoming
|
||||
'django_notify',
|
||||
|
||||
@@ -204,6 +204,9 @@ OPENID_USE_AS_ADMIN_LOGIN = False
|
||||
|
||||
OPENID_PROVIDER_TRUSTED_ROOTS = ['*']
|
||||
|
||||
############################## OAUTH2 Provider ################################
|
||||
FEATURES['ENABLE_OAUTH2_PROVIDER'] = True
|
||||
|
||||
######################## MIT Certificates SSL Auth ############################
|
||||
|
||||
FEATURES['AUTH_USE_CERTIFICATES'] = False
|
||||
|
||||
@@ -217,6 +217,9 @@ OPENID_UPDATE_DETAILS_FROM_SREG = True
|
||||
OPENID_USE_AS_ADMIN_LOGIN = False
|
||||
OPENID_PROVIDER_TRUSTED_ROOTS = ['*']
|
||||
|
||||
############################## OAUTH2 Provider ################################
|
||||
FEATURES['ENABLE_OAUTH2_PROVIDER'] = True
|
||||
|
||||
###################### Payment ##############################3
|
||||
# Enable fake payment processing page
|
||||
FEATURES['ENABLE_PAYMENT_FAKE'] = True
|
||||
|
||||
@@ -40,11 +40,13 @@
|
||||
{% block body %}{% endblock %}
|
||||
{% block bodyextra %}{% endblock %}
|
||||
</div>
|
||||
{% if IS_EDX_DOMAIN %}
|
||||
{% if IS_REQUEST_IN_MICROSITE %}
|
||||
{# For now we don't support overriden Django templates in microsites. Leave footer blank for now which is better than saying Edx.#}
|
||||
{% elif IS_EDX_DOMAIN %}
|
||||
{% if ENABLE_NEW_EDX_FOOTER %}
|
||||
{% include "edx_footer.html" %}
|
||||
{% include "footer-edx-new.html" %}
|
||||
{% else %}
|
||||
{% include "original_edx_footer.html" %}
|
||||
{% include "footer-edx.html" %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% include "footer.html" %}
|
||||
|
||||
@@ -459,6 +459,12 @@ if settings.FEATURES.get('AUTH_USE_OPENID_PROVIDER'):
|
||||
url(r'^openid/provider/xrds/$', 'external_auth.views.provider_xrds', name='openid-provider-xrds')
|
||||
)
|
||||
|
||||
if settings.FEATURES.get('ENABLE_OAUTH2_PROVIDER'):
|
||||
urlpatterns += (
|
||||
url(r'^oauth2/', include('oauth2_provider.urls', namespace='oauth2')),
|
||||
)
|
||||
|
||||
|
||||
if settings.FEATURES.get('ENABLE_LMS_MIGRATION'):
|
||||
urlpatterns += (
|
||||
url(r'^migrate/modules$', 'lms_migration.migrate.manage_modulestores'),
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
-e git+https://github.com/edx/django-staticfiles.git@d89aae2a82f2b#egg=django-staticfiles
|
||||
-e git+https://github.com/edx/django-pipeline.git@88ec8a011e481918fdc9d2682d4017c835acd8be#egg=django-pipeline
|
||||
-e git+https://github.com/edx/django-wiki.git@cd0b2b31997afccde519fe5b3365e61a9edb143f#egg=django-wiki
|
||||
-e git+https://github.com/edx/django-oauth2-provider.git@0.2.7-fork-edx-1#egg=django-oauth2-provider
|
||||
-e git+https://github.com/gabrielfalcao/lettuce.git@cccc3978ad2df82a78b6f9648fe2e9baddd22f88#egg=lettuce
|
||||
-e git+https://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev
|
||||
-e git+https://github.com/eventbrite/zendesk.git@d53fe0e81b623f084e91776bcf6369f8b7b63879#egg=zendesk
|
||||
@@ -31,3 +32,4 @@
|
||||
-e git+https://github.com/edx/opaque-keys.git@d45d0bd8d64c69531be69178b9505b5d38806ce0#egg=opaque-keys
|
||||
-e git+https://github.com/edx/ease.git@97de68448e5495385ba043d3091f570a699d5b5f#egg=ease
|
||||
-e git+https://github.com/edx/i18n-tools.git@56f048af9b6868613c14aeae760548834c495011#egg=i18n-tools
|
||||
-e git+https://github.com/edx/edx-oauth2-provider.git@0.2.1#egg=oauth2-provider
|
||||
|
||||
Reference in New Issue
Block a user