Merge pull request #10361 from edx/mobile/refactor-has-access
Refactor has_access for re-usability.
This commit is contained in:
@@ -10,7 +10,7 @@ Note: The access control logic in this file does NOT check for enrollment in
|
||||
If enrollment is to be checked, use get_course_with_access in courseware.courses.
|
||||
It is a wrapper around has_access that additionally checks for enrollment.
|
||||
"""
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import pytz
|
||||
|
||||
@@ -31,7 +31,6 @@ from xmodule.error_module import ErrorDescriptor
|
||||
from xmodule.x_module import XModule, DEPRECATION_VSCOMPAT_EVENT
|
||||
from xmodule.split_test_module import get_split_user_partitions
|
||||
from xmodule.partitions.partitions import NoSuchUserPartitionError, NoSuchUserPartitionGroupError
|
||||
from xmodule.util.django import get_current_request_hostname
|
||||
|
||||
from external_auth.models import ExternalAuthMap
|
||||
from courseware.masquerade import get_masquerade_role, is_masquerading_as_student
|
||||
@@ -57,26 +56,15 @@ from ccx_keys.locator import CCXLocator
|
||||
import dogstats_wrapper as dog_stats_api
|
||||
|
||||
from courseware.access_response import (
|
||||
AccessResponse,
|
||||
MilestoneError,
|
||||
MobileAvailabilityError,
|
||||
StartDateError,
|
||||
VisibilityError,
|
||||
)
|
||||
|
||||
DEBUG_ACCESS = False
|
||||
ACCESS_GRANTED = AccessResponse(True)
|
||||
ACCESS_DENIED = AccessResponse(False)
|
||||
from courseware.access_utils import adjust_start_date, check_start_date, debug, ACCESS_GRANTED, ACCESS_DENIED
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def debug(*args, **kwargs):
|
||||
# to avoid overly verbose output, this is off by default
|
||||
if DEBUG_ACCESS:
|
||||
log.debug(*args, **kwargs)
|
||||
|
||||
|
||||
def has_access(user, action, obj, course_key=None):
|
||||
"""
|
||||
Check whether a user has the access to do action on obj. Handles any magic
|
||||
@@ -173,24 +161,7 @@ def _can_access_descriptor_with_start_date(user, descriptor, course_key): # pyl
|
||||
AccessResponse: The result of this access check. Possible results are
|
||||
ACCESS_GRANTED or a StartDateError.
|
||||
"""
|
||||
start_dates_disabled = settings.FEATURES['DISABLE_START_DATES']
|
||||
if start_dates_disabled and not is_masquerading_as_student(user, course_key):
|
||||
return ACCESS_GRANTED
|
||||
else:
|
||||
now = datetime.now(UTC())
|
||||
effective_start = _adjust_start_date_for_beta_testers(
|
||||
user,
|
||||
descriptor,
|
||||
course_key=course_key
|
||||
)
|
||||
if (
|
||||
descriptor.start is None
|
||||
or now > effective_start
|
||||
or in_preview_mode()
|
||||
):
|
||||
return ACCESS_GRANTED
|
||||
|
||||
return StartDateError(descriptor.start)
|
||||
return check_start_date(user, descriptor.days_early_for_beta, descriptor.start, course_key)
|
||||
|
||||
|
||||
def _can_view_courseware_with_prerequisites(user, course): # pylint: disable=invalid-name
|
||||
@@ -698,7 +669,7 @@ def _dispatch(table, action, user, obj):
|
||||
type(obj), action))
|
||||
|
||||
|
||||
def _adjust_start_date_for_beta_testers(user, descriptor, course_key=None): # pylint: disable=invalid-name
|
||||
def _adjust_start_date_for_beta_testers(user, descriptor, course_key): # pylint: disable=invalid-name,unused-argument
|
||||
"""
|
||||
If user is in a beta test group, adjust the start date by the appropriate number of
|
||||
days.
|
||||
@@ -717,21 +688,8 @@ def _adjust_start_date_for_beta_testers(user, descriptor, course_key=None): # p
|
||||
NOTE: For now, this function assumes that the descriptor's location is in the course
|
||||
the user is looking at. Once we have proper usages and definitions per the XBlock
|
||||
design, this should use the course the usage is in.
|
||||
|
||||
NOTE: If testing manually, make sure FEATURES['DISABLE_START_DATES'] = False
|
||||
in envs/dev.py!
|
||||
"""
|
||||
if descriptor.days_early_for_beta is None:
|
||||
# bail early if no beta testing is set up
|
||||
return descriptor.start
|
||||
|
||||
if CourseBetaTesterRole(course_key).has_user(user):
|
||||
debug("Adjust start time: user in beta role for %s", descriptor)
|
||||
delta = timedelta(descriptor.days_early_for_beta)
|
||||
effective = descriptor.start - delta
|
||||
return effective
|
||||
|
||||
return descriptor.start
|
||||
return adjust_start_date(user, descriptor.days_early_for_beta, descriptor.start, course_key)
|
||||
|
||||
|
||||
def _has_instructor_access_to_location(user, location, course_key=None):
|
||||
@@ -898,11 +856,3 @@ def get_user_role(user, course_key):
|
||||
return 'staff'
|
||||
else:
|
||||
return 'student'
|
||||
|
||||
|
||||
def in_preview_mode():
|
||||
"""
|
||||
Returns whether the user is in preview mode or not.
|
||||
"""
|
||||
hostname = get_current_request_hostname()
|
||||
return bool(hostname and settings.PREVIEW_DOMAIN in hostname.split('.'))
|
||||
|
||||
78
lms/djangoapps/courseware/access_utils.py
Normal file
78
lms/djangoapps/courseware/access_utils.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""
|
||||
Simple utility functions for computing access.
|
||||
It allows us to share code between access.py and block transformers.
|
||||
"""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from django.conf import settings
|
||||
from django.utils.timezone import UTC
|
||||
from logging import getLogger
|
||||
from student.roles import CourseBetaTesterRole
|
||||
from courseware.masquerade import is_masquerading_as_student
|
||||
from courseware.access_response import AccessResponse, StartDateError
|
||||
from xmodule.util.django import get_current_request_hostname
|
||||
|
||||
|
||||
DEBUG_ACCESS = False
|
||||
log = getLogger(__name__)
|
||||
|
||||
ACCESS_GRANTED = AccessResponse(True)
|
||||
ACCESS_DENIED = AccessResponse(False)
|
||||
|
||||
|
||||
def debug(*args, **kwargs):
|
||||
"""
|
||||
Helper function for local debugging.
|
||||
"""
|
||||
# to avoid overly verbose output, this is off by default
|
||||
if DEBUG_ACCESS:
|
||||
log.debug(*args, **kwargs)
|
||||
|
||||
|
||||
def adjust_start_date(user, days_early_for_beta, start, course_key):
|
||||
"""
|
||||
If user is in a beta test group, adjust the start date by the appropriate number of
|
||||
days.
|
||||
|
||||
Returns:
|
||||
A datetime. Either the same as start, or earlier for beta testers.
|
||||
"""
|
||||
if days_early_for_beta is None:
|
||||
# bail early if no beta testing is set up
|
||||
return start
|
||||
|
||||
if CourseBetaTesterRole(course_key).has_user(user):
|
||||
debug("Adjust start time: user in beta role for %s", course_key)
|
||||
delta = timedelta(days_early_for_beta)
|
||||
effective = start - delta
|
||||
return effective
|
||||
|
||||
return start
|
||||
|
||||
|
||||
def check_start_date(user, days_early_for_beta, start, course_key):
|
||||
"""
|
||||
Verifies whether the given user is allowed access given the
|
||||
start date and the Beta offset for the given course.
|
||||
|
||||
Returns:
|
||||
AccessResponse: Either ACCESS_GRANTED or StartDateError.
|
||||
"""
|
||||
start_dates_disabled = settings.FEATURES['DISABLE_START_DATES']
|
||||
if start_dates_disabled and not is_masquerading_as_student(user, course_key):
|
||||
return ACCESS_GRANTED
|
||||
else:
|
||||
now = datetime.now(UTC())
|
||||
effective_start = adjust_start_date(user, days_early_for_beta, start, course_key)
|
||||
if start is None or now > effective_start or in_preview_mode():
|
||||
return ACCESS_GRANTED
|
||||
|
||||
return StartDateError(start)
|
||||
|
||||
|
||||
def in_preview_mode():
|
||||
"""
|
||||
Returns whether the user is in preview mode or not.
|
||||
"""
|
||||
hostname = get_current_request_hostname()
|
||||
return bool(hostname and settings.PREVIEW_DOMAIN in hostname.split('.'))
|
||||
@@ -207,7 +207,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
|
||||
|
||||
@ddt.data(None, YESTERDAY, TOMORROW)
|
||||
@patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
|
||||
@patch('courseware.access.get_current_request_hostname', Mock(return_value='preview.localhost'))
|
||||
@patch('courseware.access_utils.get_current_request_hostname', Mock(return_value='preview.localhost'))
|
||||
def test__has_access_descriptor_in_preview_mode(self, start):
|
||||
"""
|
||||
Tests that descriptor has access in preview mode.
|
||||
@@ -225,7 +225,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase):
|
||||
) # ddt throws an error if I don't put the None argument there
|
||||
@ddt.unpack
|
||||
@patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
|
||||
@patch('courseware.access.get_current_request_hostname', Mock(return_value='localhost'))
|
||||
@patch('courseware.access_utils.get_current_request_hostname', Mock(return_value='localhost'))
|
||||
def test__has_access_descriptor_when_not_in_preview_mode(self, start, expected_error_type):
|
||||
"""
|
||||
Tests that descriptor has no access when start date in future & without preview.
|
||||
|
||||
@@ -27,8 +27,9 @@ from django.db import transaction
|
||||
from markupsafe import escape
|
||||
|
||||
from courseware import grades
|
||||
from courseware.access import has_access, in_preview_mode, _adjust_start_date_for_beta_testers
|
||||
from courseware.access import has_access, _adjust_start_date_for_beta_testers
|
||||
from courseware.access_response import StartDateError
|
||||
from courseware.access_utils import in_preview_mode
|
||||
from courseware.courses import (
|
||||
get_courses, get_course, get_course_by_id,
|
||||
get_studio_url, get_course_with_access,
|
||||
|
||||
Reference in New Issue
Block a user