Unverified Commit 8bd9c14f by Albert (AJ) St. Aubin Committed by GitHub

Merge pull request #16660 from edx/aj/entitlement_api_permissions

Updated the permissions to all non staff users to get their entitlements
parents 53fea6a4 dbbe52ab
"""
This module provides a custom DRF Permission class for supporting SAFE_METHODS to Authenticated Users, but
requiring Superuser access for all other Request types on an API endpoint.
"""
from rest_framework.permissions import BasePermission, SAFE_METHODS
class IsAdminOrAuthenticatedReadOnly(BasePermission):
"""
Method that will require staff access for all methods not
in the SAFE_METHODS list. For example GET requests will not
require a Staff or Admin user.
"""
def has_permission(self, request, view):
if request.method in SAFE_METHODS:
return request.user.is_authenticated
else:
return request.user.is_staff
......@@ -6,6 +6,7 @@ from django.conf import settings
from django.core.urlresolvers import reverse
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from student.tests.factories import CourseEnrollmentFactory, UserFactory, TEST_PASSWORD
# Entitlements is not in CMS' INSTALLED_APPS so these imports will error during test collection
......@@ -42,11 +43,11 @@ class EntitlementViewSetTest(ModuleStoreTestCase):
response = self.client.get(self.entitlements_list_url)
assert response.status_code == 401
def test_staff_user_required(self):
def test_staff_user_not_required_for_get(self):
not_staff_user = UserFactory()
self.client.login(username=not_staff_user.username, password=UserFactory._DEFAULT_PASSWORD)
self.client.login(username=not_staff_user.username, password=TEST_PASSWORD)
response = self.client.get(self.entitlements_list_url)
assert response.status_code == 403
assert response.status_code == 200
def test_add_entitlement_with_missing_data(self):
entitlement_data_missing_parts = self._get_data_set(self.user, str(uuid.uuid4()))
......@@ -60,6 +61,33 @@ class EntitlementViewSetTest(ModuleStoreTestCase):
)
assert response.status_code == 400
def test_staff_user_required_for_post(self):
not_staff_user = UserFactory()
self.client.login(username=not_staff_user.username, password=TEST_PASSWORD)
course_uuid = uuid.uuid4()
entitlement_data = self._get_data_set(self.user, str(course_uuid))
response = self.client.post(
self.entitlements_list_url,
data=json.dumps(entitlement_data),
content_type='application/json',
)
assert response.status_code == 403
def test_staff_user_required_for_delete(self):
not_staff_user = UserFactory()
self.client.login(username=not_staff_user.username, password=TEST_PASSWORD)
course_entitlement = CourseEntitlementFactory()
url = reverse(self.ENTITLEMENTS_DETAILS_PATH, args=[str(course_entitlement.uuid)])
response = self.client.delete(
url,
content_type='application/json',
)
assert response.status_code == 403
def test_add_entitlement(self):
course_uuid = uuid.uuid4()
entitlement_data = self._get_data_set(self.user, str(course_uuid))
......@@ -78,7 +106,21 @@ class EntitlementViewSetTest(ModuleStoreTestCase):
)
assert results == CourseEntitlementSerializer(course_entitlement).data
def test_get_entitlements(self):
def test_non_staff_get_select_entitlements(self):
not_staff_user = UserFactory()
self.client.login(username=not_staff_user.username, password=TEST_PASSWORD)
CourseEntitlementFactory.create_batch(2)
entitlement = CourseEntitlementFactory.create(user=not_staff_user)
response = self.client.get(
self.entitlements_list_url,
content_type='application/json',
)
assert response.status_code == 200
results = response.data.get('results', []) # pylint: disable=no-member
assert results == CourseEntitlementSerializer([entitlement], many=True).data
def test_staff_get_all_entitlements(self):
entitlements = CourseEntitlementFactory.create_batch(2)
response = self.client.get(
......@@ -109,7 +151,6 @@ class EntitlementViewSetTest(ModuleStoreTestCase):
entitlement = CourseEntitlementFactory()
CourseEntitlementFactory.create_batch(2)
CourseEntitlementFactory()
url = reverse(self.ENTITLEMENTS_DETAILS_PATH, args=[str(entitlement.uuid)])
response = self.client.get(
......
......@@ -4,26 +4,32 @@ from django.utils import timezone
from django_filters.rest_framework import DjangoFilterBackend
from edx_rest_framework_extensions.authentication import JwtAuthentication
from rest_framework import permissions, viewsets
from rest_framework.authentication import SessionAuthentication
from entitlements.api.v1.filters import CourseEntitlementFilter
from entitlements.models import CourseEntitlement
from entitlements.api.v1.permissions import IsAdminOrAuthenticatedReadOnly
from entitlements.api.v1.serializers import CourseEntitlementSerializer
from entitlements.models import CourseEntitlement
from openedx.core.djangoapps.cors_csrf.authentication import SessionAuthenticationCrossDomainCsrf
from student.models import CourseEnrollment
log = logging.getLogger(__name__)
class EntitlementViewSet(viewsets.ModelViewSet):
authentication_classes = (JwtAuthentication, SessionAuthentication,)
permission_classes = (permissions.IsAuthenticated, permissions.IsAdminUser,)
queryset = CourseEntitlement.objects.all().select_related('user')
authentication_classes = (JwtAuthentication, SessionAuthenticationCrossDomainCsrf,)
permission_classes = (permissions.IsAuthenticated, IsAdminOrAuthenticatedReadOnly,)
lookup_value_regex = '[0-9a-f-]+'
lookup_field = 'uuid'
serializer_class = CourseEntitlementSerializer
filter_backends = (DjangoFilterBackend,)
filter_class = CourseEntitlementFilter
def get_queryset(self):
user = self.request.user
if user.is_staff:
return CourseEntitlement.objects.all().select_related('user')
return CourseEntitlement.objects.filter(user=user).select_related('user')
def perform_destroy(self, instance):
"""
This method is an override and is called by the DELETE method
......
import string
import uuid
from uuid import uuid4
import factory
from factory.fuzzy import FuzzyChoice, FuzzyText
from entitlements.models import CourseEntitlement
from student.tests.factories import UserFactory
from course_modes.helpers import CourseMode
class CourseEntitlementFactory(factory.django.DjangoModelFactory):
class Meta(object):
model = CourseEntitlement
course_uuid = uuid.uuid4()
mode = FuzzyChoice(['verified', 'profesional'])
uuid = factory.LazyFunction(uuid4)
course_uuid = factory.LazyFunction(uuid4)
mode = FuzzyChoice([CourseMode.VERIFIED, CourseMode.PROFESSIONAL])
user = factory.SubFactory(UserFactory)
order_number = FuzzyText(prefix='TEXTX', chars=string.digits)
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