Merge pull request #30741 from openedx/hammad/ENT-5992

feat: added support to check for DSC in course-home:course-metadata view.
This commit is contained in:
Hammad Ahmad Waqas
2022-07-25 14:08:59 +05:00
committed by GitHub
5 changed files with 93 additions and 37 deletions

View File

@@ -99,32 +99,51 @@ class CourseHomeMetadataTests(BaseCourseHomeTests):
'enroll_user': True,
'instructor_role': False,
'masquerade_role': None,
'dsc_required': False,
'expect_course_access': True,
'error_code': None,
},
{
# Un-enrolled learners should NOT have access.
'enroll_user': False,
'instructor_role': False,
'masquerade_role': None,
'dsc_required': False,
'expect_course_access': False,
'error_code': 'enrollment_required'
},
{
# Un-enrolled instructors should have access.
'enroll_user': False,
'instructor_role': True,
'masquerade_role': None,
'dsc_required': False,
'expect_course_access': True,
'error_code': None
},
{
# Un-enrolled instructors masquerading as students should have access.
'enroll_user': False,
'instructor_role': True,
'masquerade_role': 'student',
'dsc_required': False,
'expect_course_access': True,
'error_code': None
},
{
# Data sharing Consent required learners should Not have access.
'enroll_user': True,
'instructor_role': False,
'masquerade_role': None,
'dsc_required': True,
'expect_course_access': False,
'error_code': 'data_sharing_access_required'
}
)
@ddt.unpack
def test_course_access(self, enroll_user, instructor_role, masquerade_role, expect_course_access):
def test_course_access(
self, enroll_user, instructor_role, masquerade_role, dsc_required, expect_course_access, error_code
):
"""
Test that course_access is calculated correctly based on
access to MFE and access to the course itself.
@@ -136,13 +155,12 @@ class CourseHomeMetadataTests(BaseCourseHomeTests):
if masquerade_role:
self.update_masquerade(role=masquerade_role)
response = self.client.get(self.url)
consent_url = 'dump/consent/url' if dsc_required else None
with mock.patch('openedx.features.enterprise_support.api.get_enterprise_consent_url', return_value=consent_url):
response = self.client.get(self.url)
assert response.status_code == 200
if expect_course_access:
assert response.data['course_access']['has_access']
else:
assert not response.data['course_access']['has_access']
assert response.data['course_access']['has_access'] == expect_course_access
assert response.data['course_access']['error_code'] == error_code
# Start date is used when handling some errors, so make sure it is present too
assert response.data['start'] == self.course.start.isoformat() + 'Z'

View File

@@ -88,6 +88,7 @@ class CourseHomeMetadataView(RetrieveAPIView):
'load',
check_if_enrolled=True,
check_if_authenticated=True,
check_if_dsc_required=True,
)
_, request.user = setup_masquerade(

View File

@@ -227,6 +227,17 @@ class EnrollmentRequiredAccessError(AccessError):
super().__init__(error_code, developer_message, user_message)
class DataSharingConsentRequiredAccessError(AccessError):
"""
Access denied because the user must give Data sharing consent before access it.
"""
def __init__(self, consent_url):
error_code = "data_sharing_access_required"
developer_message = consent_url
user_message = _("You must give Data Sharing Consent for the course")
super().__init__(error_code, developer_message, user_message)
class AuthenticationRequiredAccessError(AccessError):
"""
Access denied because the user must be authenticated to see it

View File

@@ -7,25 +7,23 @@ It allows us to share code between access.py and block transformers.
from datetime import datetime, timedelta
from logging import getLogger
from crum import get_current_request
from django.conf import settings
from pytz import UTC
from lms.djangoapps.courseware.access_response import (
AccessResponse,
StartDateError,
EnrollmentRequiredAccessError,
AuthenticationRequiredAccessError,
)
from lms.djangoapps.courseware.masquerade import get_course_masquerade, is_masquerading_as_student
from openedx.core.djangoapps.util.user_messages import PageLevelMessages # lint-amnesty, pylint: disable=unused-import
from openedx.core.djangolib.markup import HTML # lint-amnesty, pylint: disable=unused-import
from openedx.features.course_experience import (
COURSE_PRE_START_ACCESS_FLAG,
COURSE_ENABLE_UNENROLLED_ACCESS_FLAG,
)
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.roles import CourseBetaTesterRole
from xmodule.util.xmodule_django import get_current_request_hostname # lint-amnesty, pylint: disable=wrong-import-order
from lms.djangoapps.courseware.access_response import (
AccessResponse,
AuthenticationRequiredAccessError,
DataSharingConsentRequiredAccessError,
EnrollmentRequiredAccessError,
StartDateError
)
from lms.djangoapps.courseware.masquerade import get_course_masquerade, is_masquerading_as_student
from openedx.features.course_experience import COURSE_ENABLE_UNENROLLED_ACCESS_FLAG, COURSE_PRE_START_ACCESS_FLAG
from xmodule.course_module import COURSE_VISIBILITY_PUBLIC # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.util.xmodule_django import get_current_request_hostname # lint-amnesty, pylint: disable=wrong-import-order
DEBUG_ACCESS = False
log = getLogger(__name__)
@@ -168,3 +166,23 @@ def check_public_access(course, visibilities):
return ACCESS_GRANTED
return ACCESS_DENIED
def check_data_sharing_consent(course_id):
"""
Grants access if the user is do not need DataSharing consent, otherwise returns data sharing link.
Returns:
AccessResponse: Either ACCESS_GRANTED or DataSharingConsentRequiredAccessError
"""
from openedx.features.enterprise_support.api import get_enterprise_consent_url
consent_url = get_enterprise_consent_url(
request=get_current_request(),
course_id=course_id,
return_to='courseware',
enrollment_exists=True,
source='CoursewareAccess'
)
if consent_url:
return DataSharingConsentRequiredAccessError(consent_url=consent_url)
return ACCESS_GRANTED

View File

@@ -19,17 +19,21 @@ from fs.errors import ResourceNotFound
from opaque_keys.edx.keys import UsageKey
from path import Path as path
from openedx.core.lib.cache_utils import request_cached
from common.djangoapps.edxmako.shortcuts import render_to_string
from common.djangoapps.static_replace import replace_static_urls
from common.djangoapps.util.date_utils import strftime_localized
from lms.djangoapps import branding
from lms.djangoapps.course_blocks.api import get_course_blocks
from lms.djangoapps.courseware.access import has_access
from lms.djangoapps.courseware.access_response import (
AuthenticationRequiredAccessError,
EnrollmentRequiredAccessError,
MilestoneAccessError,
OldMongoAccessError,
StartDateError,
StartDateError
)
from lms.djangoapps.courseware.access_utils import check_authentication, check_data_sharing_consent, check_enrollment
from lms.djangoapps.courseware.courseware_access_exception import CoursewareAccessException
from lms.djangoapps.courseware.date_summary import (
CertificateAvailableDate,
CourseAssignmentDate,
@@ -40,29 +44,20 @@ from lms.djangoapps.courseware.date_summary import (
VerificationDeadlineDate,
VerifiedUpgradeDeadlineDate
)
from lms.djangoapps.courseware.exceptions import CourseRunNotFound
from lms.djangoapps.courseware.exceptions import CourseAccessRedirect, CourseRunNotFound
from lms.djangoapps.courseware.masquerade import check_content_start_date_for_masquerade_user
from lms.djangoapps.courseware.model_data import FieldDataCache
from lms.djangoapps.courseware.module_render import get_module
from common.djangoapps.edxmako.shortcuts import render_to_string
from lms.djangoapps.courseware.access_utils import (
check_authentication,
check_enrollment,
)
from lms.djangoapps.courseware.courseware_access_exception import CoursewareAccessException
from lms.djangoapps.courseware.exceptions import CourseAccessRedirect
from lms.djangoapps.course_blocks.api import get_course_blocks
from lms.djangoapps.survey.utils import SurveyRequiredAccessError, check_survey_required_and_unanswered
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.enrollments.api import get_course_enrollment_details
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.lib.api.view_utils import LazySequence
from openedx.core.lib.cache_utils import request_cached
from openedx.core.lib.courses import get_course_by_id
from openedx.features.course_duration_limits.access import AuditExpiredError
from openedx.features.course_experience import RELATIVE_DATES_FLAG
from openedx.features.course_experience.utils import is_block_structure_complete_for_assignments
from common.djangoapps.static_replace import replace_static_urls
from lms.djangoapps.survey.utils import SurveyRequiredAccessError, check_survey_required_and_unanswered
from common.djangoapps.util.date_utils import strftime_localized
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.exceptions import ItemNotFoundError # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.x_module import STUDENT_VIEW # lint-amnesty, pylint: disable=wrong-import-order
@@ -135,7 +130,15 @@ def get_course_overview_with_access(user, action, course_key, check_if_enrolled=
return course_overview
def check_course_access(course, user, action, check_if_enrolled=False, check_survey_complete=True, check_if_authenticated=False): # lint-amnesty, pylint: disable=line-too-long
def check_course_access(
course,
user,
action,
check_if_enrolled=False,
check_survey_complete=True,
check_if_authenticated=False,
check_if_dsc_required=False,
):
"""
Check that the user has the access to perform the specified action
on the course (CourseBlock|CourseOverview).
@@ -161,6 +164,11 @@ def check_course_access(course, user, action, check_if_enrolled=False, check_sur
if not enrollment_access_response:
return enrollment_access_response
if check_if_dsc_required:
data_sharing_consent_response = check_data_sharing_consent(course)
if not data_sharing_consent_response:
return data_sharing_consent_response
# Redirect if the user must answer a survey before entering the course.
if check_survey_complete and action == 'load':
survey_access_response = check_survey_required_and_unanswered(user, course)