""" Tests for EmbargoMiddleware with CountryAccessRules """ import unittest from mock import patch 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 @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' @patch.dict(settings.FEATURES, {'EMBARGO': True}) def setUp(self): super(EmbargoMiddlewareAccessTests, self).setUp('embargo') 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)