Merge pull request #15361 from edx/iivic/LEARNER-1523
Created new embargo check api
This commit is contained in:
@@ -1133,7 +1133,7 @@ class EnrollmentEmbargoTest(EnrollmentTestMixin, UrlResetMixin, ModuleStoreTestC
|
||||
self.user.profile.country = restricted_country.country
|
||||
self.user.profile.save()
|
||||
|
||||
path = reverse('embargo_blocked_message', kwargs={'access_point': 'enrollment', 'message_key': 'default'})
|
||||
path = reverse('embargo:blocked_message', kwargs={'access_point': 'enrollment', 'message_key': 'default'})
|
||||
self.assert_access_denied(path)
|
||||
|
||||
@override_settings(EDX_API_KEY=EnrollmentTestMixin.API_KEY)
|
||||
|
||||
@@ -813,7 +813,8 @@ urlpatterns += (
|
||||
# Embargo
|
||||
if settings.FEATURES.get('EMBARGO'):
|
||||
urlpatterns += (
|
||||
url(r'^embargo/', include('openedx.core.djangoapps.embargo.urls')),
|
||||
url(r'^embargo/', include('openedx.core.djangoapps.embargo.urls', namespace='embargo')),
|
||||
url(r'^api/embargo/', include('openedx.core.djangoapps.embargo.urls', namespace='api_embargo')),
|
||||
)
|
||||
|
||||
# Survey Djangoapp
|
||||
|
||||
@@ -98,7 +98,7 @@ class EmbargoMiddleware(object):
|
||||
# If the IP is blacklisted, reject.
|
||||
# This applies to any request, not just courseware URLs.
|
||||
ip_blacklist_url = reverse(
|
||||
'embargo_blocked_message',
|
||||
'embargo:blocked_message',
|
||||
kwargs={
|
||||
'access_point': 'courseware',
|
||||
'message_key': 'embargo'
|
||||
|
||||
@@ -317,7 +317,7 @@ class RestrictedCourse(models.Model):
|
||||
# We use generic messaging unless we find something more specific,
|
||||
# but *always* return a valid URL path.
|
||||
default_path = reverse(
|
||||
'embargo_blocked_message',
|
||||
'embargo:blocked_message',
|
||||
kwargs={
|
||||
'access_point': 'courseware',
|
||||
'message_key': 'default'
|
||||
@@ -336,7 +336,7 @@ class RestrictedCourse(models.Model):
|
||||
course = cls.objects.get(course_key=course_key)
|
||||
msg_key = course.message_key_for_access_point(access_point)
|
||||
return reverse(
|
||||
'embargo_blocked_message',
|
||||
'embargo:blocked_message',
|
||||
kwargs={
|
||||
'access_point': access_point,
|
||||
'message_key': msg_key
|
||||
|
||||
@@ -75,7 +75,7 @@ def restrict_course(course_key, access_point="enrollment", disable_access_check=
|
||||
# Yield the redirect url so the tests don't need to know
|
||||
# the embargo messaging URL structure.
|
||||
redirect_url = reverse(
|
||||
'embargo_blocked_message',
|
||||
'embargo:blocked_message',
|
||||
kwargs={
|
||||
'access_point': access_point,
|
||||
'message_key': 'default'
|
||||
|
||||
30
openedx/core/djangoapps/embargo/tests/factories.py
Normal file
30
openedx/core/djangoapps/embargo/tests/factories.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import factory
|
||||
from factory.django import DjangoModelFactory
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
from ..models import Country, CountryAccessRule, RestrictedCourse
|
||||
|
||||
|
||||
class CountryFactory(DjangoModelFactory):
|
||||
class Meta(object):
|
||||
model = Country
|
||||
|
||||
country = 'US'
|
||||
|
||||
|
||||
class RestrictedCourseFactory(DjangoModelFactory):
|
||||
class Meta(object):
|
||||
model = RestrictedCourse
|
||||
|
||||
@factory.lazy_attribute
|
||||
def course_key(self):
|
||||
return CourseFactory().id
|
||||
|
||||
|
||||
class CountryAccessRuleFactory(DjangoModelFactory):
|
||||
class Meta(object):
|
||||
model = CountryAccessRule
|
||||
|
||||
country = factory.SubFactory(CountryFactory)
|
||||
restricted_course = factory.SubFactory(RestrictedCourseFactory)
|
||||
rule_type = CountryAccessRule.BLACKLIST_RULE
|
||||
@@ -115,7 +115,7 @@ class EmbargoMiddlewareAccessTests(UrlResetMixin, ModuleStoreTestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
else:
|
||||
redirect_url = reverse(
|
||||
'embargo_blocked_message',
|
||||
'embargo:blocked_message',
|
||||
kwargs={
|
||||
'access_point': 'courseware',
|
||||
'message_key': 'embargo'
|
||||
@@ -139,7 +139,7 @@ class EmbargoMiddlewareAccessTests(UrlResetMixin, ModuleStoreTestCase):
|
||||
)
|
||||
|
||||
url = reverse(
|
||||
'embargo_blocked_message',
|
||||
'embargo:blocked_message',
|
||||
kwargs={
|
||||
'access_point': access_point,
|
||||
'message_key': msg_key
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
"""Tests for embargo app views. """
|
||||
|
||||
from mock import patch
|
||||
import ddt
|
||||
import json
|
||||
import mock
|
||||
import pygeoip
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
import ddt
|
||||
from mock import patch
|
||||
|
||||
from util.testing import UrlResetMixin
|
||||
from .factories import CountryFactory, CountryAccessRuleFactory, RestrictedCourseFactory
|
||||
from .. import messages
|
||||
from lms.djangoapps.course_api.tests.mixins import CourseApiFactoryMixin
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
|
||||
from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme
|
||||
from student.tests.factories import UserFactory
|
||||
from util.testing import UrlResetMixin
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
|
||||
|
||||
@skip_unless_lms
|
||||
@@ -57,7 +66,7 @@ class CourseAccessMessageViewTest(CacheIsolationTestCase, UrlResetMixin):
|
||||
# Custom override specified for the "embargo" message
|
||||
# for backwards compatibility with previous versions
|
||||
# of the embargo app.
|
||||
url = reverse('embargo_blocked_message', kwargs={
|
||||
url = reverse('embargo:blocked_message', kwargs={
|
||||
'access_point': access_point,
|
||||
'message_key': "embargo"
|
||||
})
|
||||
@@ -69,7 +78,7 @@ class CourseAccessMessageViewTest(CacheIsolationTestCase, UrlResetMixin):
|
||||
|
||||
def _load_page(self, access_point, message_key, expected_status=200):
|
||||
"""Load the message page and check the status code. """
|
||||
url = reverse('embargo_blocked_message', kwargs={
|
||||
url = reverse('embargo:blocked_message', kwargs={
|
||||
'access_point': access_point,
|
||||
'message_key': message_key
|
||||
})
|
||||
@@ -85,3 +94,56 @@ class CourseAccessMessageViewTest(CacheIsolationTestCase, UrlResetMixin):
|
||||
actual=response.status_code
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@skip_unless_lms
|
||||
class CheckCourseAccessViewTest(CourseApiFactoryMixin, ModuleStoreTestCase):
|
||||
""" Tests the course access check endpoint. """
|
||||
|
||||
@patch.dict(settings.FEATURES, {'EMBARGO': True})
|
||||
def setUp(self):
|
||||
super(CheckCourseAccessViewTest, self).setUp()
|
||||
self.url = reverse('api_embargo:v1_course_access')
|
||||
user = UserFactory(is_staff=True)
|
||||
self.client.login(username=user.username, password=UserFactory._DEFAULT_PASSWORD)
|
||||
self.course_id = str(CourseFactory().id)
|
||||
self.request_data = {
|
||||
'course_ids': [self.course_id],
|
||||
'ip_address': '0.0.0.0',
|
||||
'user': self.user,
|
||||
}
|
||||
|
||||
def test_course_access_endpoint_with_unrestricted_course(self):
|
||||
response = self.client.get(self.url, data=self.request_data)
|
||||
expected_response = {'access': True}
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data, expected_response)
|
||||
|
||||
def test_course_access_endpoint_with_restricted_course(self):
|
||||
CountryAccessRuleFactory(restricted_course=RestrictedCourseFactory(course_key=self.course_id))
|
||||
|
||||
self.user.is_staff = False
|
||||
self.user.save()
|
||||
# Appear to make a request from an IP in the blocked country
|
||||
with mock.patch.object(pygeoip.GeoIP, 'country_code_by_addr') as mock_ip:
|
||||
mock_ip.return_value = 'US'
|
||||
response = self.client.get(self.url, data=self.request_data)
|
||||
expected_response = {'access': False}
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data, expected_response)
|
||||
|
||||
def test_course_access_endpoint_with_logged_out_user(self):
|
||||
self.client.logout()
|
||||
response = self.client.get(self.url, data=self.request_data)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
def test_course_access_endpoint_with_non_staff_user(self):
|
||||
user = UserFactory(is_staff=False)
|
||||
self.client.login(username=user.username, password=UserFactory._DEFAULT_PASSWORD)
|
||||
|
||||
response = self.client.get(self.url, data=self.request_data)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
def test_course_access_endpoint_with_invalid_data(self):
|
||||
response = self.client.get(self.url, data=None)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
|
||||
from django.conf.urls import patterns, url
|
||||
|
||||
from .views import CourseAccessMessageView
|
||||
from .views import CheckCourseAccessView, CourseAccessMessageView
|
||||
|
||||
urlpatterns = patterns(
|
||||
'openedx.core.djangoapps.embargo.views',
|
||||
url(
|
||||
r'^blocked-message/(?P<access_point>enrollment|courseware)/(?P<message_key>.+)/$',
|
||||
CourseAccessMessageView.as_view(),
|
||||
name='embargo_blocked_message',
|
||||
name='blocked_message',
|
||||
),
|
||||
url(r'^v1/course_access/$', CheckCourseAccessView.as_view(), name='v1_course_access'),
|
||||
)
|
||||
|
||||
@@ -1,11 +1,54 @@
|
||||
"""Views served by the embargo app. """
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.http import Http404
|
||||
from django.views.generic.base import View
|
||||
from edx_rest_framework_extensions.authentication import JwtAuthentication
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from rest_framework import permissions, status
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from edxmako.shortcuts import render_to_response
|
||||
|
||||
from . import messages
|
||||
from .api import check_course_access
|
||||
|
||||
|
||||
class CheckCourseAccessView(APIView):
|
||||
permission_classes = (permissions.IsAuthenticated, permissions.IsAdminUser)
|
||||
|
||||
def get(self, request):
|
||||
"""
|
||||
GET /api/embargo/v1/course_access/
|
||||
|
||||
Arguments:
|
||||
request (HttpRequest)
|
||||
|
||||
Return:
|
||||
Response: True or False depending on the check.
|
||||
|
||||
"""
|
||||
course_ids = request.GET.getlist('course_ids', [])
|
||||
username = request.GET.get('user')
|
||||
user_ip_address = request.GET.get('ip_address')
|
||||
|
||||
try:
|
||||
user = User.objects.get(username=username)
|
||||
except User.DoesNotExist:
|
||||
user = None
|
||||
|
||||
response = {'access': True}
|
||||
|
||||
if course_ids and user and user_ip_address:
|
||||
for course_id in course_ids:
|
||||
if not check_course_access(CourseKey.from_string(course_id), user, user_ip_address):
|
||||
response['access'] = False
|
||||
break
|
||||
else:
|
||||
return Response(data=None, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
return Response(response)
|
||||
|
||||
|
||||
class CourseAccessMessageView(View):
|
||||
|
||||
Reference in New Issue
Block a user