208 lines
7.3 KiB
Python
208 lines
7.3 KiB
Python
"""
|
|
Tests for EmbargoMiddleware with CountryAccessRules
|
|
"""
|
|
|
|
import unittest
|
|
from mock import patch
|
|
from nose.plugins.attrib import attr
|
|
import ddt
|
|
|
|
from django.core.urlresolvers import reverse
|
|
from django.conf import settings
|
|
from django.core.cache import cache as django_cache
|
|
|
|
from util.testing import UrlResetMixin
|
|
from student.tests.factories import UserFactory
|
|
from xmodule.modulestore.tests.factories import CourseFactory
|
|
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
|
from config_models.models import cache as config_cache
|
|
|
|
from embargo.models import RestrictedCourse, IPFilter
|
|
from embargo.test_utils import restrict_course
|
|
|
|
|
|
@attr(shard=3)
|
|
@ddt.ddt
|
|
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
|
|
class EmbargoMiddlewareAccessTests(UrlResetMixin, ModuleStoreTestCase):
|
|
"""Tests of embargo middleware country access rules.
|
|
|
|
There are detailed unit tests for the rule logic in
|
|
`test_api.py`; here, we're mainly testing the integration
|
|
with middleware
|
|
|
|
"""
|
|
USERNAME = 'fred'
|
|
PASSWORD = 'secret'
|
|
|
|
URLCONF_MODULES = ['embargo']
|
|
|
|
@patch.dict(settings.FEATURES, {'EMBARGO': True})
|
|
def setUp(self):
|
|
super(EmbargoMiddlewareAccessTests, self).setUp()
|
|
self.user = UserFactory(username=self.USERNAME, password=self.PASSWORD)
|
|
self.course = CourseFactory.create()
|
|
self.client.login(username=self.USERNAME, password=self.PASSWORD)
|
|
|
|
self.courseware_url = reverse(
|
|
'course_root',
|
|
kwargs={'course_id': unicode(self.course.id)}
|
|
)
|
|
self.non_courseware_url = reverse('dashboard')
|
|
|
|
# Clear the cache to avoid interference between tests
|
|
django_cache.clear()
|
|
config_cache.clear()
|
|
|
|
@patch.dict(settings.FEATURES, {'EMBARGO': True})
|
|
@ddt.data(True, False)
|
|
def test_blocked(self, disable_access_check):
|
|
with restrict_course(self.course.id, access_point='courseware', disable_access_check=disable_access_check) as redirect_url: # pylint: disable=line-too-long
|
|
response = self.client.get(self.courseware_url)
|
|
if disable_access_check:
|
|
self.assertEqual(response.status_code, 200)
|
|
else:
|
|
self.assertRedirects(response, redirect_url)
|
|
|
|
@patch.dict(settings.FEATURES, {'EMBARGO': True})
|
|
def test_allowed(self):
|
|
# Add the course to the list of restricted courses
|
|
# but don't create any access rules
|
|
RestrictedCourse.objects.create(course_key=self.course.id)
|
|
|
|
# Expect that we can access courseware
|
|
response = self.client.get(self.courseware_url)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
@patch.dict(settings.FEATURES, {'EMBARGO': True})
|
|
def test_non_courseware_url(self):
|
|
with restrict_course(self.course.id):
|
|
response = self.client.get(self.non_courseware_url)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
@patch.dict(settings.FEATURES, {'EMBARGO': True})
|
|
@ddt.data(
|
|
# request_ip, blacklist, whitelist, is_enabled, allow_access
|
|
('173.194.123.35', ['173.194.123.35'], [], True, False),
|
|
('173.194.123.35', ['173.194.0.0/16'], [], True, False),
|
|
('173.194.123.35', ['127.0.0.0/32', '173.194.0.0/16'], [], True, False),
|
|
('173.195.10.20', ['173.194.0.0/16'], [], True, True),
|
|
('173.194.123.35', ['173.194.0.0/16'], ['173.194.0.0/16'], True, False),
|
|
('173.194.123.35', [], ['173.194.0.0/16'], True, True),
|
|
('192.178.2.3', [], ['173.194.0.0/16'], True, True),
|
|
('173.194.123.35', ['173.194.123.35'], [], False, True),
|
|
)
|
|
@ddt.unpack
|
|
def test_ip_access_rules(self, request_ip, blacklist, whitelist, is_enabled, allow_access):
|
|
# Ensure that IP blocking works for anonymous users
|
|
self.client.logout()
|
|
|
|
# Set up the IP rules
|
|
IPFilter.objects.create(
|
|
blacklist=", ".join(blacklist),
|
|
whitelist=", ".join(whitelist),
|
|
enabled=is_enabled
|
|
)
|
|
|
|
# Check that access is enforced
|
|
response = self.client.get(
|
|
"/",
|
|
HTTP_X_FORWARDED_FOR=request_ip,
|
|
REMOTE_ADDR=request_ip
|
|
)
|
|
|
|
if allow_access:
|
|
self.assertEqual(response.status_code, 200)
|
|
else:
|
|
redirect_url = reverse(
|
|
'embargo_blocked_message',
|
|
kwargs={
|
|
'access_point': 'courseware',
|
|
'message_key': 'embargo'
|
|
}
|
|
)
|
|
self.assertRedirects(response, redirect_url)
|
|
|
|
@patch.dict(settings.FEATURES, {'EMBARGO': True})
|
|
@ddt.data(
|
|
('courseware', 'default'),
|
|
('courseware', 'embargo'),
|
|
('enrollment', 'default'),
|
|
('enrollment', 'embargo')
|
|
)
|
|
@ddt.unpack
|
|
def test_always_allow_access_to_embargo_messages(self, access_point, msg_key):
|
|
# Blacklist an IP address
|
|
IPFilter.objects.create(
|
|
blacklist="192.168.10.20",
|
|
enabled=True
|
|
)
|
|
|
|
url = reverse(
|
|
'embargo_blocked_message',
|
|
kwargs={
|
|
'access_point': access_point,
|
|
'message_key': msg_key
|
|
}
|
|
)
|
|
response = self.client.get(
|
|
url,
|
|
HTTP_X_FORWARDED_FOR="192.168.10.20",
|
|
REMOTE_ADDR="192.168.10.20"
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
@patch.dict(settings.FEATURES, {'EMBARGO': True})
|
|
def test_whitelist_ip_skips_country_access_checks(self):
|
|
# Whitelist an IP address
|
|
IPFilter.objects.create(
|
|
whitelist="192.168.10.20",
|
|
enabled=True
|
|
)
|
|
|
|
# Set up country access rules so the user would
|
|
# be restricted from the course.
|
|
with restrict_course(self.course.id):
|
|
# Make a request from the whitelisted IP address
|
|
response = self.client.get(
|
|
self.courseware_url,
|
|
HTTP_X_FORWARDED_FOR="192.168.10.20",
|
|
REMOTE_ADDR="192.168.10.20"
|
|
)
|
|
|
|
# Expect that we were still able to access the page,
|
|
# even though we would have been blocked by country
|
|
# access rules.
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
@patch.dict(settings.FEATURES, {'EMBARGO': True})
|
|
def test_always_allow_course_detail_access(self):
|
|
""" Access to the Course Structure API's course detail endpoint should always be granted. """
|
|
# Make the user staff so that it has permissions to access the views.
|
|
self.user.is_staff = True
|
|
self.user.save() # pylint: disable=no-member
|
|
|
|
# Blacklist an IP address
|
|
ip_address = "192.168.10.20"
|
|
IPFilter.objects.create(
|
|
blacklist=ip_address,
|
|
enabled=True
|
|
)
|
|
|
|
url = reverse('course_structure_api:v0:detail', kwargs={'course_id': unicode(self.course.id)})
|
|
response = self.client.get(
|
|
url,
|
|
HTTP_X_FORWARDED_FOR=ip_address,
|
|
REMOTE_ADDR=ip_address
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# Test with a fully-restricted course
|
|
with restrict_course(self.course.id):
|
|
response = self.client.get(
|
|
url,
|
|
HTTP_X_FORWARDED_FOR=ip_address,
|
|
REMOTE_ADDR=ip_address
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|