Skip to content

Commit

Permalink
Django2: student admin views permission fix
Browse files Browse the repository at this point in the history
  • Loading branch information
nasthagiri committed Apr 2, 2020
1 parent 18c5333 commit 9c60bd6
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 29 deletions.
77 changes: 48 additions & 29 deletions common/djangoapps/student/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,32 @@
COURSE_ENROLLMENT_ADMIN_SWITCH = WaffleSwitch(STUDENT_WAFFLE_NAMESPACE, 'courseenrollment_admin')


class _Check(object):
"""
A method decorator that pre-emptively returns false if a feature is disabled.
Otherwise, it returns the return value of the decorated method.
To use, add this decorator above a method and pass in a function that returns
a boolean indicating whether the feature is enabled.
Example:
@_Check.is_enabled(FEATURE_TOGGLE.is_enabled)
"""
@classmethod
def is_enabled(cls, is_enabled_func):
"""
See above docstring.
"""
def inner(func):
@wraps(func)
def decorator(*args, **kwargs):
if not is_enabled_func():
return False
return func(*args, **kwargs)
return decorator
return inner


class CourseAccessRoleForm(forms.ModelForm):
"""Form for adding new Course Access Roles view the Django Admin Panel."""

Expand Down Expand Up @@ -238,37 +264,40 @@ def get_search_results(self, request, queryset, search_term):
def queryset(self, request):
return super(CourseEnrollmentAdmin, self).queryset(request).select_related('user')

def has_permission(self, request, method):
@_Check.is_enabled(COURSE_ENROLLMENT_ADMIN_SWITCH.is_enabled)
def has_view_permission(self, request, obj=None):
"""
Returns True if the given admin method is allowed.
Returns True if CourseEnrollment objects can be viewed via the admin view.
"""
if COURSE_ENROLLMENT_ADMIN_SWITCH.is_enabled():
return getattr(super(CourseEnrollmentAdmin, self), method)(request)
return False
return super(CourseEnrollmentAdmin, self).has_view_permission(request, obj) # pylint: disable=no-member

@_Check.is_enabled(COURSE_ENROLLMENT_ADMIN_SWITCH.is_enabled)
def has_add_permission(self, request):
"""
Returns True if CourseEnrollment objects can be added via the admin view.
"""
return self.has_permission(request, 'has_add_permission')
return super(CourseEnrollmentAdmin, self).has_add_permission(request)

@_Check.is_enabled(COURSE_ENROLLMENT_ADMIN_SWITCH.is_enabled)
def has_change_permission(self, request, obj=None):
"""
Returns True if CourseEnrollment objects can be modified via the admin view.
"""
return self.has_permission(request, 'has_change_permission')
return super(CourseEnrollmentAdmin, self).has_change_permission(request, obj)

@_Check.is_enabled(COURSE_ENROLLMENT_ADMIN_SWITCH.is_enabled)
def has_delete_permission(self, request, obj=None):
"""
Returns True if CourseEnrollment objects can be deleted via the admin view.
"""
return self.has_permission(request, 'has_delete_permission')
return super(CourseEnrollmentAdmin, self).has_delete_permission(request, obj)

@_Check.is_enabled(COURSE_ENROLLMENT_ADMIN_SWITCH.is_enabled)
def has_module_permission(self, request):
"""
Returns True if links to the CourseEnrollment admin view can be displayed.
"""
return self.has_permission(request, 'has_module_permission')
return super(CourseEnrollmentAdmin, self).has_module_permission(request)


class UserProfileInline(admin.StackedInline):
Expand Down Expand Up @@ -351,45 +380,35 @@ class LoginFailuresAdmin(admin.ModelAdmin):
actions = ['unlock_student_accounts']
change_form_template = 'admin/student/loginfailures/change_form_template.html'

class _Feature(object):
@_Check.is_enabled(LoginFailures.is_feature_enabled)
def has_module_permission(self, request):
"""
Inner feature class to implement decorator.
Only enabled if feature is enabled.
"""
@classmethod
def is_enabled(cls, func):
"""
Check if feature is enabled.
"""
@wraps(func)
def decorator(*args, **kwargs):
"""Decorator class to return"""
if not LoginFailures.is_feature_enabled():
return False
return func(*args, **kwargs)
return decorator
return super(LoginFailuresAdmin, self).has_module_permission(request)

@_Feature.is_enabled
def has_module_permission(self, request):
@_Check.is_enabled(LoginFailures.is_feature_enabled)
def has_view_permission(self, request, obj=None):
"""
Only enabled if feature is enabled.
"""
return super(LoginFailuresAdmin, self).has_module_permission(request)
return super(LoginFailuresAdmin, self).has_view_permission(request, obj) # pylint: disable=no-member

@_Feature.is_enabled
@_Check.is_enabled(LoginFailures.is_feature_enabled)
def has_delete_permission(self, request, obj=None):
"""
Only enabled if feature is enabled.
"""
return super(LoginFailuresAdmin, self).has_delete_permission(request, obj)

@_Feature.is_enabled
@_Check.is_enabled(LoginFailures.is_feature_enabled)
def has_change_permission(self, request, obj=None):
"""
Only enabled if feature is enabled.
"""
return super(LoginFailuresAdmin, self).has_change_permission(request, obj)

@_Feature.is_enabled
@_Check.is_enabled(LoginFailures.is_feature_enabled)
def has_add_permission(self, request):
"""
Only enabled if feature is enabled.
Expand Down
6 changes: 6 additions & 0 deletions common/djangoapps/student/tests/test_admin_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,12 @@ def test_unicode_username(self):
)
self.assertEqual(str(LoginFailures.objects.get(user=self.user2)), 'Zażółć gęślą jaźń: 2 - -')

@override_settings(FEATURES={'ENABLE_MAX_FAILED_LOGIN_ATTEMPTS': True})
def test_feature_enabled(self):
url = reverse('admin:student_loginfailures_changelist')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)

@ddt.data(
reverse('admin:student_loginfailures_changelist'),
reverse('admin:student_loginfailures_add'),
Expand Down

0 comments on commit 9c60bd6

Please sign in to comment.