diff --git a/cms/djangoapps/contentstore/views/public.py b/cms/djangoapps/contentstore/views/public.py index 50ceee25a9..c850afaa58 100644 --- a/cms/djangoapps/contentstore/views/public.py +++ b/cms/djangoapps/contentstore/views/public.py @@ -10,7 +10,6 @@ from django.views.decorators.clickjacking import xframe_options_deny from django.views.decorators.csrf import ensure_csrf_cookie from edxmako.shortcuts import render_to_response -from openedx.core.djangoapps.external_auth.views import redirect_with_get, ssl_get_cert_from_request, ssl_login_shortcut from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from waffle.decorators import waffle_switch from contentstore.config import waffle @@ -27,15 +26,10 @@ def signup(request): csrf_token = csrf(request)['csrf_token'] if request.user.is_authenticated: return redirect('/course/') - if settings.FEATURES.get('AUTH_USE_CERTIFICATES_IMMEDIATE_SIGNUP'): - # Redirect to course to login to process their certificate if SSL is enabled - # and registration is disabled. - return redirect_with_get('login', request.GET, False) return render_to_response('register.html', {'csrf': csrf_token}) -@ssl_login_shortcut @ensure_csrf_cookie @xframe_options_deny def login_page(request): @@ -43,19 +37,6 @@ def login_page(request): Display the login form. """ csrf_token = csrf(request)['csrf_token'] - if (settings.FEATURES['AUTH_USE_CERTIFICATES'] and - ssl_get_cert_from_request(request)): - # SSL login doesn't require a login view, so redirect - # to course now that the user is authenticated via - # the decorator. - next_url = request.GET.get('next') - if next_url: - return redirect(next_url) - else: - return redirect('/course/') - if settings.FEATURES.get('AUTH_USE_CAS'): - # If CAS is enabled, redirect auth handling to there - return redirect(reverse('cas-login')) return render_to_response( 'login.html', diff --git a/cms/envs/aws.py b/cms/envs/aws.py index 14e76e83fa..e0c9d9526b 100644 --- a/cms/envs/aws.py +++ b/cms/envs/aws.py @@ -273,26 +273,6 @@ HEARTBEAT_CHECKS = ENV_TOKENS.get('HEARTBEAT_CHECKS', HEARTBEAT_CHECKS) HEARTBEAT_EXTENDED_CHECKS = ENV_TOKENS.get('HEARTBEAT_EXTENDED_CHECKS', HEARTBEAT_EXTENDED_CHECKS) HEARTBEAT_CELERY_TIMEOUT = ENV_TOKENS.get('HEARTBEAT_CELERY_TIMEOUT', HEARTBEAT_CELERY_TIMEOUT) -# Django CAS external authentication settings -CAS_EXTRA_LOGIN_PARAMS = ENV_TOKENS.get("CAS_EXTRA_LOGIN_PARAMS", None) -if FEATURES.get('AUTH_USE_CAS'): - CAS_SERVER_URL = ENV_TOKENS.get("CAS_SERVER_URL", None) - AUTHENTICATION_BACKENDS = [ - 'django.contrib.auth.backends.ModelBackend', - 'django_cas.backends.CASBackend', - ] - - INSTALLED_APPS.append('django_cas') - - MIDDLEWARE_CLASSES.append('django_cas.middleware.CASMiddleware') - CAS_ATTRIBUTE_CALLBACK = ENV_TOKENS.get('CAS_ATTRIBUTE_CALLBACK', None) - if CAS_ATTRIBUTE_CALLBACK: - import importlib - CAS_USER_DETAILS_RESOLVER = getattr( - importlib.import_module(CAS_ATTRIBUTE_CALLBACK['module']), - CAS_ATTRIBUTE_CALLBACK['function'] - ) - # Specific setting for the File Upload Service to store media in a bucket. FILE_UPLOAD_STORAGE_BUCKET_NAME = ENV_TOKENS.get('FILE_UPLOAD_STORAGE_BUCKET_NAME', FILE_UPLOAD_STORAGE_BUCKET_NAME) FILE_UPLOAD_STORAGE_PREFIX = ENV_TOKENS.get('FILE_UPLOAD_STORAGE_PREFIX', FILE_UPLOAD_STORAGE_PREFIX) diff --git a/cms/envs/common.py b/cms/envs/common.py index 6204937f1f..43ddc34150 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -182,8 +182,6 @@ FEATURES = { # Doing so will cause all courses to be released on production 'DISABLE_START_DATES': False, # When True, all courses will be active, regardless of start date - 'AUTH_USE_CERTIFICATES': False, - # email address for studio staff (eg to request course creation) 'STUDIO_REQUEST_EMAIL': '', @@ -1026,7 +1024,6 @@ INSTALLED_APPS = [ 'openedx.core.djangoapps.contentserver', 'course_creators', - 'openedx.core.djangoapps.external_auth', 'student.apps.StudentConfig', # misleading name due to sharing with lms 'openedx.core.djangoapps.course_groups', # not used in cms (yet), but tests run 'xblock_config.apps.XBlockConfig', diff --git a/cms/envs/production.py b/cms/envs/production.py index 9d1b70362d..86f8c6dbb7 100644 --- a/cms/envs/production.py +++ b/cms/envs/production.py @@ -279,26 +279,6 @@ HEARTBEAT_CHECKS = ENV_TOKENS.get('HEARTBEAT_CHECKS', HEARTBEAT_CHECKS) HEARTBEAT_EXTENDED_CHECKS = ENV_TOKENS.get('HEARTBEAT_EXTENDED_CHECKS', HEARTBEAT_EXTENDED_CHECKS) HEARTBEAT_CELERY_TIMEOUT = ENV_TOKENS.get('HEARTBEAT_CELERY_TIMEOUT', HEARTBEAT_CELERY_TIMEOUT) -# Django CAS external authentication settings -CAS_EXTRA_LOGIN_PARAMS = ENV_TOKENS.get("CAS_EXTRA_LOGIN_PARAMS", None) -if FEATURES.get('AUTH_USE_CAS'): - CAS_SERVER_URL = ENV_TOKENS.get("CAS_SERVER_URL", None) - AUTHENTICATION_BACKENDS = [ - 'django.contrib.auth.backends.ModelBackend', - 'django_cas.backends.CASBackend', - ] - - INSTALLED_APPS.append('django_cas') - - MIDDLEWARE_CLASSES.append('django_cas.middleware.CASMiddleware') - CAS_ATTRIBUTE_CALLBACK = ENV_TOKENS.get('CAS_ATTRIBUTE_CALLBACK', None) - if CAS_ATTRIBUTE_CALLBACK: - import importlib - CAS_USER_DETAILS_RESOLVER = getattr( - importlib.import_module(CAS_ATTRIBUTE_CALLBACK['module']), - CAS_ATTRIBUTE_CALLBACK['function'] - ) - # Specific setting for the File Upload Service to store media in a bucket. FILE_UPLOAD_STORAGE_BUCKET_NAME = ENV_TOKENS.get('FILE_UPLOAD_STORAGE_BUCKET_NAME', FILE_UPLOAD_STORAGE_BUCKET_NAME) FILE_UPLOAD_STORAGE_PREFIX = ENV_TOKENS.get('FILE_UPLOAD_STORAGE_PREFIX', FILE_UPLOAD_STORAGE_PREFIX) diff --git a/cms/urls.py b/cms/urls.py index 812e12d86b..04edae7027 100644 --- a/cms/urls.py +++ b/cms/urls.py @@ -9,7 +9,6 @@ import contentstore.views from cms.djangoapps.contentstore.views.organization import OrganizationListView import openedx.core.djangoapps.common_views.xblock import openedx.core.djangoapps.debug.views -import openedx.core.djangoapps.external_auth.views import openedx.core.djangoapps.lang_pref.views from openedx.core.djangoapps.password_policy import compliance as password_policy_compliance from openedx.core.djangoapps.password_policy.forms import PasswordPolicyAwareAdminAuthForm @@ -199,13 +198,6 @@ if settings.FEATURES.get('ENABLE_EXPORT_GIT'): if settings.FEATURES.get('ENABLE_SERVICE_STATUS'): urlpatterns.append(url(r'^status/', include('openedx.core.djangoapps.service_status.urls'))) -if settings.FEATURES.get('AUTH_USE_CAS'): - import django_cas.views - - urlpatterns += [ - url(r'^cas-auth/login/$', openedx.core.djangoapps.external_auth.views.cas_login, name="cas-login"), - url(r'^cas-auth/logout/$', django_cas.views.logout, {'next_page': '/'}, name="cas-logout"), - ] # The password pages in the admin tool are disabled so that all password # changes go through our user portal and follow complexity requirements. urlpatterns.append(url(r'^admin/password_change/$', handler404)) diff --git a/common/djangoapps/student/tests/test_password_policy.py b/common/djangoapps/student/tests/test_password_policy.py index 7b4d015165..8a93ae3949 100644 --- a/common/djangoapps/student/tests/test_password_policy.py +++ b/common/djangoapps/student/tests/test_password_policy.py @@ -3,9 +3,7 @@ This test file will verify proper password policy enforcement, which is an option feature """ import json -from importlib import import_module -from django.conf import settings from django.contrib.auth.models import AnonymousUser from django.urls import reverse from django.test import TestCase @@ -13,7 +11,6 @@ from django.test.client import RequestFactory from django.test.utils import override_settings from mock import patch -from openedx.core.djangoapps.external_auth.models import ExternalAuthMap from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory from openedx.core.djangoapps.user_authn.views.deprecated import create_account from util.password_policy_validators import create_validator_config @@ -254,31 +251,6 @@ class TestPasswordPolicy(TestCase): obj = json.loads(response.content) self.assertTrue(obj['success']) - @override_settings(AUTH_PASSWORD_VALIDATORS=[ - create_validator_config('util.password_policy_validators.MinimumLengthValidator', {'min_length': 6}) - ], SESSION_ENGINE='django.contrib.sessions.backends.cache') - def test_ext_auth_password_length_too_short(self): - """ - Tests that even if password policy is enforced, ext_auth registrations aren't subject to it - """ - self.url_params['password'] = u'aaa' # shouldn't pass validation - request = self.request_factory.post(self.url, self.url_params) - request.site = SiteFactory.create() - # now indicate we are doing ext_auth by setting 'ExternalAuthMap' in the session. - request.session = import_module(settings.SESSION_ENGINE).SessionStore() # empty session - extauth = ExternalAuthMap(external_id='withmap@stanford.edu', - external_email='withmap@stanford.edu', - internal_password=self.url_params['password'], - external_domain='shib:https://idp.stanford.edu/') - request.session['ExternalAuthMap'] = extauth - request.user = AnonymousUser() - - with patch('edxmako.request_context.get_current_request', return_value=request): - response = create_account(request) - self.assertEqual(response.status_code, 200) - obj = json.loads(response.content) - self.assertTrue(obj['success']) - class TestUsernamePasswordNonmatch(TestCase): """ diff --git a/common/djangoapps/student/views/management.py b/common/djangoapps/student/views/management.py index c07b57fbb1..d1c4d12382 100644 --- a/common/djangoapps/student/views/management.py +++ b/common/djangoapps/student/views/management.py @@ -134,8 +134,7 @@ def index(request, extra_context=None, user=AnonymousUser()): """ Render the edX main page. - extra_context is used to allow immediate display of certain modal windows, eg signup, - as used by external_auth. + extra_context is used to allow immediate display of certain modal windows, eg signup. """ if extra_context is None: extra_context = {} diff --git a/common/djangoapps/util/cache.py b/common/djangoapps/util/cache.py index 7ee469d732..26ae2a6963 100644 --- a/common/djangoapps/util/cache.py +++ b/common/djangoapps/util/cache.py @@ -53,8 +53,7 @@ def cache_if_anonymous(*get_parameters): # If that page is cached the authentication doesn't # happen, so we disable the cache when that feature is enabled. if ( - not request.user.is_authenticated and - not settings.FEATURES['AUTH_USE_CERTIFICATES'] + not request.user.is_authenticated ): # Use the cache. The same view accessed through different domain names may # return different things, so include the domain name in the key. diff --git a/lms/djangoapps/branding/views.py b/lms/djangoapps/branding/views.py index f9a7232963..d5cb206036 100644 --- a/lms/djangoapps/branding/views.py +++ b/lms/djangoapps/branding/views.py @@ -43,16 +43,6 @@ def index(request): settings.FEATURES.get('ALWAYS_REDIRECT_HOMEPAGE_TO_DASHBOARD_FOR_AUTHENTICATED_USER', True)): return redirect(reverse('dashboard')) - if settings.FEATURES.get('AUTH_USE_CERTIFICATES'): - from openedx.core.djangoapps.external_auth.views import ssl_login - # Set next URL to dashboard if it isn't set to avoid - # caching a redirect to / that causes a redirect loop on logout - if not request.GET.get('next'): - req_new = request.GET.copy() - req_new['next'] = reverse('dashboard') - request.GET = req_new - return ssl_login(request) - enable_mktg_site = configuration_helpers.get_value( 'ENABLE_MKTG_SITE', settings.FEATURES.get('ENABLE_MKTG_SITE', False) diff --git a/lms/djangoapps/courseware/access.py b/lms/djangoapps/courseware/access.py index 6626e9ced7..ccdac8abc3 100644 --- a/lms/djangoapps/courseware/access.py +++ b/lms/djangoapps/courseware/access.py @@ -43,7 +43,6 @@ from lms.djangoapps.ccx.custom_exception import CCXLocatorValidationException from lms.djangoapps.ccx.models import CustomCourseForEdX from mobile_api.models import IgnoreMobileAvailableFlagConfig from openedx.core.djangoapps.content.course_overviews.models import CourseOverview -from openedx.core.djangoapps.external_auth.models import ExternalAuthMap from openedx.features.course_duration_limits.access import check_course_expired from student import auth from student.models import CourseEnrollmentAllowed @@ -248,22 +247,10 @@ def _can_enroll_courselike(user, courselike): Returns: AccessResponse, indicating whether the user can enroll. """ - enrollment_domain = courselike.enrollment_domain # Courselike objects (e.g., course descriptors and CourseOverviews) have an attribute named `id` # which actually points to a CourseKey. Sigh. course_key = courselike.id - # If using a registration method to restrict enrollment (e.g., Shibboleth) - if settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD') and enrollment_domain: - if user is not None and user.is_authenticated and \ - ExternalAuthMap.objects.filter(user=user, external_domain=enrollment_domain): - debug("Allow: external_auth of " + enrollment_domain) - reg_method_ok = True - else: - reg_method_ok = False - else: - reg_method_ok = True - # If the user appears in CourseEnrollmentAllowed paired with the given course key, # they may enroll, except if the CEA has already been used by a different user. # Note that as dictated by the legacy database schema, the filter call includes @@ -289,7 +276,7 @@ def _can_enroll_courselike(user, courselike): now = datetime.now(UTC) enrollment_start = courselike.enrollment_start or datetime.min.replace(tzinfo=UTC) enrollment_end = courselike.enrollment_end or datetime.max.replace(tzinfo=UTC) - if reg_method_ok and enrollment_start < now < enrollment_end: + if enrollment_start < now < enrollment_end: debug("Allow: in enrollment period") return ACCESS_GRANTED diff --git a/lms/djangoapps/courseware/tests/test_about.py b/lms/djangoapps/courseware/tests/test_about.py index 1f6a483c63..92032597e3 100644 --- a/lms/djangoapps/courseware/tests/test_about.py +++ b/lms/djangoapps/courseware/tests/test_about.py @@ -390,46 +390,6 @@ class AboutWithInvitationOnly(SharedModuleStoreTestCase): self.assertIn(REG_STR, resp.content) -@patch.dict(settings.FEATURES, {'RESTRICT_ENROLL_BY_REG_METHOD': True}) -class AboutTestCaseShibCourse(LoginEnrollmentTestCase, SharedModuleStoreTestCase): - """ - Test cases covering about page behavior for courses that use shib enrollment domain ("shib courses") - """ - @classmethod - def setUpClass(cls): - super(AboutTestCaseShibCourse, cls).setUpClass() - cls.course = CourseFactory.create(enrollment_domain="shib:https://idp.stanford.edu/") - cls.about = ItemFactory.create( - category="about", parent_location=cls.course.location, - data="OOGIE BLOOGIE", display_name="overview" - ) - - def test_logged_in_shib_course(self): - """ - For shib courses, logged in users will see the enroll button, but get rejected once they click there - """ - self.setup_user() - url = reverse('about_course', args=[text_type(self.course.id)]) - resp = self.client.get(url) - self.assertEqual(resp.status_code, 200) - self.assertIn("OOGIE BLOOGIE", resp.content) - self.assertIn(u"Enroll in {}".format(self.course.id.course), resp.content.decode('utf-8')) - self.assertIn(SHIB_ERROR_STR, resp.content) - self.assertIn(REG_STR, resp.content) - - def test_anonymous_user_shib_course(self): - """ - For shib courses, anonymous users will also see the enroll button - """ - url = reverse('about_course', args=[text_type(self.course.id)]) - resp = self.client.get(url) - self.assertEqual(resp.status_code, 200) - self.assertIn("OOGIE BLOOGIE", resp.content) - self.assertIn(u"Enroll in {}".format(self.course.id.course), resp.content.decode('utf-8')) - self.assertIn(SHIB_ERROR_STR, resp.content) - self.assertIn(REG_STR, resp.content) - - class AboutWithClosedEnrollment(ModuleStoreTestCase): """ This test case will check the About page for a course that has enrollment start/end diff --git a/lms/djangoapps/dashboard/management/commands/tests/test_git_add_course.py b/lms/djangoapps/dashboard/management/commands/tests/test_git_add_course.py index 2954bee812..7b03f3eb21 100644 --- a/lms/djangoapps/dashboard/management/commands/tests/test_git_add_course.py +++ b/lms/djangoapps/dashboard/management/commands/tests/test_git_add_course.py @@ -37,9 +37,6 @@ TEST_MONGODB_LOG = { 'db': 'test_xlog', } -FEATURES_WITH_SSL_AUTH = settings.FEATURES.copy() -FEATURES_WITH_SSL_AUTH['AUTH_USE_CERTIFICATES'] = True - @override_settings( MONGODB_LOG=TEST_MONGODB_LOG, diff --git a/lms/djangoapps/dashboard/sysadmin.py b/lms/djangoapps/dashboard/sysadmin.py index e42a9ac6de..abf1a466fb 100644 --- a/lms/djangoapps/dashboard/sysadmin.py +++ b/lms/djangoapps/dashboard/sysadmin.py @@ -12,14 +12,11 @@ import subprocess import mongoengine from django.conf import settings -from django.contrib.auth import authenticate from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User -from django.core.exceptions import PermissionDenied from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator from django.db import IntegrityError from django.http import Http404, HttpResponse -from django.utils import timezone from django.utils.decorators import method_decorator from django.utils.html import escape from django.utils.translation import ugettext as _ @@ -37,8 +34,6 @@ from courseware.courses import get_course_by_id from dashboard.git_import import GitImportError from dashboard.models import CourseImportLog from edxmako.shortcuts import render_to_response -from openedx.core.djangoapps.external_auth.models import ExternalAuthMap -from openedx.core.djangoapps.user_api.accounts.utils import generate_password from openedx.core.djangolib.markup import HTML from student.models import CourseEnrollment, Registration, UserProfile from student.roles import CourseInstructorRole, CourseStaffRole @@ -115,82 +110,24 @@ class Users(SysadminDashboardView): courses loaded, and user statistics """ - def fix_external_auth_map_passwords(self): - """ - This corrects any passwords that have drifted from eamap to - internal django auth. Needs to be removed when fixed in external_auth - """ - - msg = '' - for eamap in ExternalAuthMap.objects.all(): - euser = eamap.user - epass = eamap.internal_password - if euser is None: - continue - try: - testuser = authenticate(username=euser.username, password=epass) - except (TypeError, PermissionDenied, AttributeError) as err: - # Translators: This message means that the user could not be authenticated (that is, we could - # not log them in for some reason - maybe they don't have permission, or their password was wrong) - msg += _(u'Failed in authenticating {username}, error {error}\n').format( - username=euser, - error=err - ) - continue - if testuser is None: - # Translators: This message means that the user could not be authenticated (that is, we could - # not log them in for some reason - maybe they don't have permission, or their password was wrong) - msg += _(u'Failed in authenticating {username}\n').format(username=euser) - # Translators: this means that the password has been corrected (sometimes the database needs to be resynchronized) - # Translate this as meaning "the password was fixed" or "the password was corrected". - msg += _('fixed password') - euser.set_password(epass) - euser.save() - continue - if not msg: - # Translators: this means everything happened successfully, yay! - msg = _('All ok!') - return msg - def create_user(self, uname, name, password=None): - """ Creates a user (both SSL and regular)""" + """ Creates a user """ if not uname: return _('Must provide username') if not name: return _('Must provide full name') - email_domain = getattr(settings, 'SSL_AUTH_EMAIL_DOMAIN', 'MIT.EDU') - msg = u'' - if settings.FEATURES['AUTH_USE_CERTIFICATES']: - if '@' not in uname: - email = '{0}@{1}'.format(uname, email_domain) - else: - email = uname - if not email.endswith('@{0}'.format(email_domain)): - # Translators: Domain is an email domain, such as "@gmail.com" - msg += _(u'Email address must end in {domain}').format(domain="@{0}".format(email_domain)) - return msg - mit_domain = 'ssl:MIT' - if ExternalAuthMap.objects.filter(external_id=email, - external_domain=mit_domain): - msg += _(u'Failed - email {email_addr} already exists as {external_id}').format( - email_addr=email, - external_id="external_id" - ) - return msg - new_password = generate_password() - else: - if not password: - return _('Password must be supplied if not using certificates') + if not password: + return _('Password must be supplied') - email = uname + email = uname - if '@' not in email: - msg += _('email address required (not username)') - return msg - new_password = password + if '@' not in email: + msg += _('email address required (not username)') + return msg + new_password = password user = User(username=uname, email=email, is_active=True) user.set_password(new_password) @@ -210,22 +147,6 @@ class Users(SysadminDashboardView): profile.name = name profile.save() - if settings.FEATURES['AUTH_USE_CERTIFICATES']: - credential_string = getattr(settings, 'SSL_AUTH_DN_FORMAT_STRING', - '/C=US/ST=Massachusetts/O=Massachusetts Institute of Technology/OU=Client CA v1/CN={0}/emailAddress={1}') - credentials = credential_string.format(name, email) - eamap = ExternalAuthMap( - external_id=email, - external_email=email, - external_domain=mit_domain, - external_name=name, - internal_password=new_password, - external_credentials=json.dumps(credentials), - ) - eamap.user = user - eamap.dtsignup = timezone.now() - eamap.save() - msg += _(u'User {user} created successfully!').format(user=user) return msg @@ -303,12 +224,6 @@ class Users(SysadminDashboardView): (User.objects.all().iterator())) return self.return_csv('users_{0}.csv'.format( request.META['SERVER_NAME']), header, data) - elif action == 'repair_eamap': - self.msg = HTML(u'

{0}

{1}
{2}').format( - _('Repair Results'), - self.fix_external_auth_map_passwords(), - self.msg) - self.datatable = {} elif action == 'create_user': uname = request.POST.get('student_uname', '').strip() name = request.POST.get('student_fullname', '').strip() diff --git a/lms/djangoapps/dashboard/tests/test_sysadmin.py b/lms/djangoapps/dashboard/tests/test_sysadmin.py index 1acf797b74..61ad286d43 100644 --- a/lms/djangoapps/dashboard/tests/test_sysadmin.py +++ b/lms/djangoapps/dashboard/tests/test_sysadmin.py @@ -21,6 +21,7 @@ from six import text_type from dashboard.git_import import GitImportErrorNoDir from dashboard.models import CourseImportLog +from openedx.core.djangolib.markup import Text from student.roles import CourseStaffRole, GlobalStaff from student.tests.factories import UserFactory from util.date_utils import DEFAULT_DATE_TIME_FORMAT, get_time_display @@ -39,9 +40,6 @@ TEST_MONGODB_LOG = { 'db': 'test_xlog', } -FEATURES_WITH_SSL_AUTH = settings.FEATURES.copy() -FEATURES_WITH_SSL_AUTH['AUTH_USE_CERTIFICATES'] = True - class SysadminBaseTestCase(SharedModuleStoreTestCase): """ @@ -156,7 +154,7 @@ class TestSysAdminMongoCourseImport(SysadminBaseTestCase): # Create git loaded course response = self._add_edx4edx() - self.assertIn(escape(text_type(GitImportErrorNoDir(settings.GIT_REPO_DIR))), + self.assertIn(Text(text_type(GitImportErrorNoDir(settings.GIT_REPO_DIR))), response.content.decode('UTF-8')) def test_mongo_course_add_delete(self): diff --git a/lms/envs/aws.py b/lms/envs/aws.py index e4b75802d2..9d6bde5551 100644 --- a/lms/envs/aws.py +++ b/lms/envs/aws.py @@ -382,26 +382,6 @@ SSL_AUTH_DN_FORMAT_STRING = ENV_TOKENS.get( u"/C=US/ST=Massachusetts/O=Massachusetts Institute of Technology/OU=Client CA v1/CN={0}/emailAddress={1}" ) -# Django CAS external authentication settings -CAS_EXTRA_LOGIN_PARAMS = ENV_TOKENS.get("CAS_EXTRA_LOGIN_PARAMS", None) -if FEATURES.get('AUTH_USE_CAS'): - CAS_SERVER_URL = ENV_TOKENS.get("CAS_SERVER_URL", None) - AUTHENTICATION_BACKENDS = [ - 'django.contrib.auth.backends.ModelBackend', - 'django_cas.backends.CASBackend', - ] - - INSTALLED_APPS.append('django_cas') - - MIDDLEWARE_CLASSES.append('django_cas.middleware.CASMiddleware') - CAS_ATTRIBUTE_CALLBACK = ENV_TOKENS.get('CAS_ATTRIBUTE_CALLBACK', None) - if CAS_ATTRIBUTE_CALLBACK: - import importlib - CAS_USER_DETAILS_RESOLVER = getattr( - importlib.import_module(CAS_ATTRIBUTE_CALLBACK['module']), - CAS_ATTRIBUTE_CALLBACK['function'] - ) - # Video Caching. Pairing country codes with CDN URLs. # Example: {'CN': 'http://api.xuetangx.com/edx/video?s3_url='} VIDEO_CDN_URL = ENV_TOKENS.get('VIDEO_CDN_URL', {}) diff --git a/lms/envs/common.py b/lms/envs/common.py index 859fce1d93..b015946259 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -108,19 +108,6 @@ FEATURES = { 'DISABLE_LOGIN_BUTTON': False, # used in systems where login is automatic, eg MIT SSL - # extrernal access methods - 'AUTH_USE_OPENID': False, - 'AUTH_USE_CERTIFICATES': False, - 'AUTH_USE_OPENID_PROVIDER': False, - # Even though external_auth is in common, shib assumes the LMS views / urls, so it should only be enabled - # in LMS - 'AUTH_USE_SHIB': False, - 'AUTH_USE_CAS': False, - - # This flag disables the requirement of having to agree to the TOS for users registering - # with Shib. Feature was requested by Stanford's office of general counsel - 'SHIB_DISABLE_TOS': False, - # Toggles OAuth2 authentication provider 'ENABLE_OAUTH2_PROVIDER': False, @@ -137,9 +124,6 @@ FEATURES = { # Set to hide the courses list on the Learner Dashboard if they are not enrolled in any courses yet. 'HIDE_DASHBOARD_COURSES_UNTIL_ACTIVATED': False, - # Enables ability to restrict enrollment in specific courses by the user account login method - 'RESTRICT_ENROLL_BY_REG_METHOD': False, - # enable analytics server. # WARNING: THIS SHOULD ALWAYS BE SET TO FALSE UNDER NORMAL # LMS OPERATION. See analytics.py for details about what @@ -2068,10 +2052,6 @@ INSTALLED_APPS = [ # Student support tools 'support', - # External auth (OpenID, shib) - 'openedx.core.djangoapps.external_auth', - 'django_openid_auth', - # django-oauth2-provider (deprecated) 'provider', 'provider.oauth2', @@ -2474,19 +2454,6 @@ if FEATURES.get('CLASS_DASHBOARD'): ENABLE_CREDIT_ELIGIBILITY = True FEATURES['ENABLE_CREDIT_ELIGIBILITY'] = ENABLE_CREDIT_ELIGIBILITY -######################## CAS authentication ########################### - -if FEATURES.get('AUTH_USE_CAS'): - CAS_SERVER_URL = 'https://provide_your_cas_url_here' - AUTHENTICATION_BACKENDS = [ - 'django.contrib.auth.backends.ModelBackend', - 'django_cas.backends.CASBackend', - ] - - INSTALLED_APPS.append('django_cas') - - MIDDLEWARE_CLASSES.append('django_cas.middleware.CASMiddleware') - ############# Cross-domain requests ################# if FEATURES.get('ENABLE_CORS_HEADERS'): diff --git a/lms/envs/production.py b/lms/envs/production.py index cc128c0f14..78935a190c 100644 --- a/lms/envs/production.py +++ b/lms/envs/production.py @@ -386,26 +386,6 @@ SSL_AUTH_DN_FORMAT_STRING = ENV_TOKENS.get( u"/C=US/ST=Massachusetts/O=Massachusetts Institute of Technology/OU=Client CA v1/CN={0}/emailAddress={1}" ) -# Django CAS external authentication settings -CAS_EXTRA_LOGIN_PARAMS = ENV_TOKENS.get("CAS_EXTRA_LOGIN_PARAMS", None) -if FEATURES.get('AUTH_USE_CAS'): - CAS_SERVER_URL = ENV_TOKENS.get("CAS_SERVER_URL", None) - AUTHENTICATION_BACKENDS = [ - 'django.contrib.auth.backends.ModelBackend', - 'django_cas.backends.CASBackend', - ] - - INSTALLED_APPS.append('django_cas') - - MIDDLEWARE_CLASSES.append('django_cas.middleware.CASMiddleware') - CAS_ATTRIBUTE_CALLBACK = ENV_TOKENS.get('CAS_ATTRIBUTE_CALLBACK', None) - if CAS_ATTRIBUTE_CALLBACK: - import importlib - CAS_USER_DETAILS_RESOLVER = getattr( - importlib.import_module(CAS_ATTRIBUTE_CALLBACK['module']), - CAS_ATTRIBUTE_CALLBACK['function'] - ) - # Video Caching. Pairing country codes with CDN URLs. # Example: {'CN': 'http://api.xuetangx.com/edx/video?s3_url='} VIDEO_CDN_URL = ENV_TOKENS.get('VIDEO_CDN_URL', {}) diff --git a/lms/envs/test.py b/lms/envs/test.py index 2d5caf4d29..120acfb57a 100644 --- a/lms/envs/test.py +++ b/lms/envs/test.py @@ -251,15 +251,6 @@ THIRD_PARTY_AUTH_CUSTOM_AUTH_FORMS = { }, } -################################## OPENID ##################################### -FEATURES['AUTH_USE_OPENID'] = True -FEATURES['AUTH_USE_OPENID_PROVIDER'] = True - -################################## SHIB ####################################### -FEATURES['AUTH_USE_SHIB'] = True -FEATURES['SHIB_DISABLE_TOS'] = True -FEATURES['RESTRICT_ENROLL_BY_REG_METHOD'] = True - OPENID_CREATE_USERS = False OPENID_UPDATE_DETAILS_FROM_SREG = True OPENID_USE_AS_ADMIN_LOGIN = False diff --git a/lms/templates/courseware/course_about.html b/lms/templates/courseware/course_about.html index 6104d8fc03..3328baf1d2 100644 --- a/lms/templates/courseware/course_about.html +++ b/lms/templates/courseware/course_about.html @@ -1,4 +1,4 @@ -<%page expression_filter="h" /> +<%page expression_filter="h"/> <%namespace name='static' file='../static_content.html'/> <%! from django.utils.translation import ugettext as _ @@ -8,8 +8,10 @@ from courseware.courses import get_course_about_section from django.conf import settings from six import text_type from edxmako.shortcuts import marketing_link +from openedx.core.djangolib.js_utils import js_escaped_string from openedx.core.djangolib.markup import HTML, Text from openedx.core.lib.courses import course_image_url + from six import string_types %> @@ -32,21 +34,21 @@ from six import string_types % if can_add_course_to_cart: add_course_complete_handler = function(jqXHR, textStatus) { if (jqXHR.status == 200) { - location.href = "${cart_link | n, decode.utf8}"; + location.href = "${cart_link | n, js_escaped_string}"; } if (jqXHR.status == 400) { - $("#register_error").text( - jqXHR.responseText ? jqXHR.responseText : "${_("An error occurred. Please try again later.") | n, decode.utf8}") + $("#register_error") + .text(jqXHR.responseText ? jqXHR.responseText : "${_("An error occurred. Please try again later.") | n, js_escaped_string}") .css("display", "block"); } else if (jqXHR.status == 403) { - location.href = "${reg_then_add_to_cart_link | n, decode.utf8}"; + location.href = "${reg_then_add_to_cart_link | n, js_escaped_string}"; } }; $("#add_to_cart_post").click(function(event){ $.ajax({ - url: "${reverse('add_course_to_cart', args=[text_type(course.id)]) | n, decode.utf8}", + url: "${reverse('add_course_to_cart', args=[text_type(course.id)]) | n, js_escaped_string}", type: "POST", /* Rant: HAD TO USE COMPLETE B/C PROMISE.DONE FOR SOME REASON DOES NOT WORK ON THIS PAGE. */ complete: add_course_complete_handler @@ -55,51 +57,21 @@ from six import string_types }); % endif - ## making the conditional around this entire JS block for sanity - %if settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD') and course.enrollment_domain: - <% - perms_error = Text(_('The currently logged-in user account does not have permission to enroll in this course. ' - 'You may need to {start_logout_tag}log out{end_tag} then try the enroll button again. ' - 'Please visit the {start_help_tag}help page{end_tag} for a possible solution.')).format( - start_help_tag=HTML("").format(url=marketing_link('FAQ')), end_tag=HTML(''), - start_logout_tag=HTML("").format(url=reverse('logout')) - ) - %> - $('#class_enroll_form').on('ajax:complete', function(event, xhr) { - if(xhr.status == 200) { - location.href = "${reverse('dashboard') | n, decode.utf8}"; - } else if (xhr.status == 403) { - location.href = "${reverse('course-specific-register', args=[text_type(course.id)]) | n, decode.utf8 }?course_id=${course.id | n, decode.utf8 }&enrollment_action=enroll"; - } else if (xhr.status == 400) { //This means the user did not have permission - $('#register_error').text("${perms_error | n, decode.utf8}").css("display", "block"); - } else { - $('#register_error').text( - (xhr.responseText ? xhr.responseText : "${_("An error occurred. Please try again later.") | n, decode.utf8}") - ).css("display", "block"); - } - }); - - %else: - $('#class_enroll_form').on('ajax:complete', function(event, xhr) { if(xhr.status == 200) { if (xhr.responseText == "") { - location.href = "${reverse('dashboard') | n, decode.utf8}"; + location.href = "${reverse('dashboard') | n, js_escaped_string}"; } else { location.href = xhr.responseText; } - } else if (xhr.status == 403) { - location.href = "${reverse('register_user') | n, decode.utf8 }?course_id=${course.id | n, decode.utf8 }&enrollment_action=enroll"; } else { $('#register_error').text( - (xhr.responseText ? xhr.responseText : "${_("An error occurred. Please try again later.") | n, decode.utf8}") + (xhr.responseText ? xhr.responseText : "${_("An error occurred. Please try again later.") | n, js_escaped_string}") ).css("display", "block"); } }); - %endif - })(this) @@ -138,7 +110,10 @@ from six import string_types %elif in_cart: - ${Text(_('This course is in your cart.')).format(cart_link=cart_link)} + ${Text(_('This course is in your {start_cart_link}cart{end_cart_link}.')).format( + start_cart_link=HTML('').format(cart_link=cart_link), + end_cart_link=HTML(""), + )} % elif is_course_full: @@ -167,9 +142,10 @@ from six import string_types ${Text(_("Add {course_name} to Cart {start_span}({price} USD){end_span}")).format( course_name=course.display_number_with_default, + price=course_price, start_span=HTML(""), end_span=HTML(""), - price=course_price)} + )}
%elif allow_anonymous: @@ -179,7 +155,7 @@ from six import string_types %endif %else: - <% + <% if ecommerce_checkout: reg_href = ecommerce_checkout_link else: @@ -358,7 +334,7 @@ from six import string_types
${pgettext("self","Enroll")} - +
diff --git a/lms/templates/header/navbar-not-authenticated.html b/lms/templates/header/navbar-not-authenticated.html index 9dbcf2d6af..11a48e1014 100644 --- a/lms/templates/header/navbar-not-authenticated.html +++ b/lms/templates/header/navbar-not-authenticated.html @@ -16,7 +16,6 @@ from six import text_type courses_are_browsable = settings.FEATURES.get('COURSES_ARE_BROWSABLE') allows_login = not settings.FEATURES['DISABLE_LOGIN_BUTTON'] and not combined_login_and_register can_discover_courses = settings.FEATURES.get('ENABLE_COURSE_DISCOVERY') - restrict_enroll_for_course = course and settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD') and course.enrollment_domain allow_public_account_creation = static.get_value('ALLOW_PUBLIC_ACCOUNT_CREATION', settings.FEATURES.get('ALLOW_PUBLIC_ACCOUNT_CREATION')) %>