Commit 9a573de5 by Jesse Shapiro

Add consent check to course access prerequisites; add utility functions to…

Add consent check to course access prerequisites; add utility functions to provide interface to course-specific consent in Enterprise app
parent 8a53f3b6
......@@ -2,9 +2,12 @@
Helpers to access the enterprise app
"""
from django.conf import settings
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
import logging
from django.utils.http import urlencode
try:
from enterprise.models import EnterpriseCustomer
from enterprise import utils as enterprise_utils
......@@ -13,6 +16,7 @@ try:
active_provider_enforces_data_sharing,
get_enterprise_customer_for_request,
)
from enterprise.utils import consent_necessary_for_course
except ImportError:
pass
......@@ -26,7 +30,32 @@ def enterprise_enabled():
"""
Determines whether the Enterprise app is installed
"""
return 'enterprise' in settings.INSTALLED_APPS
return 'enterprise' in settings.INSTALLED_APPS and getattr(settings, 'ENABLE_ENTERPRISE_INTEGRATION', True)
def consent_needed_for_course(user, course_id):
"""
Wrap the enterprise app check to determine if the user needs to grant
data sharing permissions before accessing a course.
"""
if not enterprise_enabled():
return False
return consent_necessary_for_course(user, course_id)
def get_course_specific_consent_url(request, course_id, return_to):
"""
Build a URL to redirect the user to the Enterprise app to provide data sharing
consent for a specific course ID.
"""
url_params = {
'course_id': course_id,
'next': request.build_absolute_uri(reverse(return_to, args=(course_id,)))
}
querystring = urlencode(url_params)
full_url = reverse('grant_data_sharing_permissions') + '?' + querystring
LOGGER.info('Redirecting to %s to complete data sharing consent', full_url)
return full_url
def data_sharing_consent_requested(request):
......
......@@ -188,6 +188,7 @@ class FieldOverridePerformanceTestCase(FieldOverrideTestMixin, ProceduralCourseT
@override_settings(
XBLOCK_FIELD_DATA_WRAPPERS=[],
MODULESTORE_FIELD_OVERRIDE_PROVIDERS=[],
ENABLE_ENTERPRISE_INTEGRATION=False,
)
def test_field_overrides(self, overrides, course_width, enable_ccx, view_as_ccx):
"""
......
......@@ -60,6 +60,31 @@ class CourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTestCase):
resp = self.client.get(url)
self.assertNotIn("You are not currently enrolled in this course", resp.content)
@mock.patch('courseware.views.views.get_course_specific_consent_url')
@mock.patch('courseware.views.views.consent_needed_for_course')
def test_redirection_missing_enterprise_consent(self, mock_consent_needed, mock_get_url):
"""
Verify that users viewing the course info who are enrolled, but have not provided
data sharing consent, are first redirected to a consent page, and then, once they've
provided consent, are able to view the course info.
"""
self.setup_user()
self.enroll(self.course)
mock_consent_needed.return_value = True
mock_get_url.return_value = reverse('dashboard')
url = reverse('info', args=[self.course.id.to_deprecated_string()])
response = self.client.get(url)
self.assertRedirects(
response,
reverse('dashboard')
)
mock_consent_needed.assert_called_once_with(self.user, unicode(self.course.id))
mock_consent_needed.return_value = False
response = self.client.get(url)
self.assertNotIn("You are not currently enrolled in this course", response.content)
def test_anonymous_user(self):
url = reverse('info', args=[self.course.id.to_deprecated_string()])
resp = self.client.get(url)
......@@ -313,7 +338,7 @@ class CourseInfoTestCaseXML(LoginEnrollmentTestCase, ModuleStoreTestCase):
@attr(shard=1)
@override_settings(FEATURES=dict(settings.FEATURES, EMBARGO=False))
@override_settings(FEATURES=dict(settings.FEATURES, EMBARGO=False), ENABLE_ENTERPRISE_INTEGRATION=False)
class SelfPacedCourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTestCase):
"""
Tests for the info page of self-paced courses.
......
......@@ -197,6 +197,29 @@ class TestViewAuth(ModuleStoreTestCase, LoginEnrollmentTestCase):
)
)
@patch('courseware.views.index.get_course_specific_consent_url')
@patch('courseware.views.index.consent_needed_for_course')
def test_redirection_missing_enterprise_consent(self, mock_consent_needed, mock_get_url):
"""
Verify that enrolled students are redirected to the Enterprise consent
URL if a linked Enterprise Customer requires data sharing consent
and it has not yet been provided.
"""
mock_consent_needed.return_value = True
mock_get_url.return_value = reverse('dashboard')
self.login(self.enrolled_user)
response = self.client.get(
reverse(
'courseware',
kwargs={'course_id': self.course.id.to_deprecated_string()}
)
)
self.assertRedirects(
response,
reverse('dashboard')
)
mock_consent_needed.assert_called_once_with(self.enrolled_user, unicode(self.course.id))
def test_instructor_page_access_nonstaff(self):
"""
Verify non-staff cannot load the instructor
......
......@@ -1130,6 +1130,7 @@ class StartDateTests(ModuleStoreTestCase):
# pylint: disable=protected-access, no-member
@attr(shard=1)
@override_settings(ENABLE_ENTERPRISE_INTEGRATION=False)
@ddt.ddt
class ProgressPageTests(ModuleStoreTestCase):
"""
......
......@@ -31,6 +31,7 @@ from shoppingcart.models import CourseRegistrationCode
from student.models import CourseEnrollment
from student.views import is_course_blocked
from student.roles import GlobalStaff
from util.enterprise_helpers import consent_needed_for_course, get_course_specific_consent_url
from util.views import ensure_valid_course_key
from xmodule.modulestore.django import modulestore
from xmodule.x_module import STUDENT_VIEW
......@@ -194,6 +195,21 @@ class CoursewareIndex(View):
self._redirect_if_needed_to_register()
self._redirect_if_needed_for_prereqs()
self._redirect_if_needed_for_course_survey()
self._redirect_if_data_sharing_consent_needed()
def _redirect_if_data_sharing_consent_needed(self):
"""
Determine if the user needs to provide data sharing consent before accessing
the course, and redirect the user to provide consent if needed.
"""
course_id = unicode(self.course_key)
if consent_needed_for_course(self.real_user, course_id):
log.warning(
u'User %s cannot access the course %s because they have not granted consent',
self.real_user,
course_id,
)
raise Redirect(get_course_specific_consent_url(self.request, course_id, 'courseware'))
def _redirect_if_needed_to_pay_for_course(self):
"""
......
......@@ -86,6 +86,7 @@ from student.roles import GlobalStaff
from util.cache import cache, cache_if_anonymous
from util.date_utils import strftime_localized
from util.db import outer_atomic
from util.enterprise_helpers import consent_needed_for_course, get_course_specific_consent_url
from util.milestones_helpers import get_prerequisite_courses_display
from util.views import _record_feedback_in_zendesk
from util.views import ensure_valid_course_key, ensure_valid_usage_key
......@@ -315,6 +316,11 @@ def course_info(request, course_id):
# to access CCX redirect him to dashboard.
return redirect(reverse('dashboard'))
# If the user is sponsored by an enterprise customer, and we still need to get data
# sharing consent, redirect to do that first.
if consent_needed_for_course(user, course_id):
return redirect(get_course_specific_consent_url(request, course_id, 'info'))
# If the user needs to take an entrance exam to access this course, then we'll need
# to send them to that specific course module before allowing them into other areas
if user_must_complete_entrance_exam(request, user, course):
......@@ -702,6 +708,11 @@ def _progress(request, course_key, student_id):
course = get_course_with_access(request.user, 'load', course_key, depth=None, check_if_enrolled=True)
prep_course_for_grading(course, request)
# If the user is sponsored by an enterprise customer, and we still need to get data
# sharing consent, redirect to do that first.
if consent_needed_for_course(request.user, unicode(course.id)):
return redirect(get_course_specific_consent_url(request, unicode(course.id), 'progress'))
# check to see if there is a required survey that must be taken before
# the user can access the course.
if survey.utils.must_answer_survey(course, request.user):
......
......@@ -48,7 +48,7 @@ edx-i18n-tools==0.3.7
edx-lint==0.4.3
edx-django-oauth2-provider==1.1.4
edx-django-sites-extensions==2.1.1
edx-enterprise==0.19.1
edx-enterprise==0.21.0
edx-oauth2-provider==1.2.0
edx-opaque-keys==0.4.0
edx-organizations==0.4.2
......
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