def process_request(self, request): """ Processes embargo requests """ url = request.path course_id = course_id_from_url(url) # If they're trying to access a course that cares about embargoes if EmbargoedCourse.is_embargoed(course_id): # If we're having performance issues, add caching here ip_addr = get_ip(request) # if blacklisted, immediately fail if ip_addr in IPFilter.current().blacklist_ips: log.info("Embargo: Restricting IP address %s to course %s because IP is blacklisted.", ip_addr, course_id) return redirect('embargo') country_code_from_ip = pygeoip.GeoIP(settings.GEOIP_PATH).country_code_by_addr(ip_addr) is_embargoed = country_code_from_ip in EmbargoedState.current().embargoed_countries_list # Fail if country is embargoed and the ip address isn't explicitly whitelisted if is_embargoed and ip_addr not in IPFilter.current().whitelist_ips: log.info( "Embargo: Restricting IP address %s to course %s because IP is from country %s.", ip_addr, course_id, country_code_from_ip ) return redirect('embargo')
def _is_embargoed_by_ip(self, ip_addr, course_id=u"", course_is_embargoed=False): """ Check whether the user is embargoed based on the IP address. Args: ip_addr (str): The IP address the request originated from. Keyword Args: course_id (unicode): The course the user is trying to access. course_is_embargoed (boolean): Whether the course the user is accessing has been embargoed. Returns: A unicode message if the user is embargoed, otherwise `None` """ # If blacklisted, immediately fail if ip_addr in IPFilter.current().blacklist_ips: return self.REASONS['ip_blacklist'].format( ip_addr=ip_addr, from_course=self._from_course_msg(course_id, course_is_embargoed) ) # If we're white-listed, then allow access if ip_addr in IPFilter.current().whitelist_ips: return None # Retrieve the country code from the IP address # and check it against the list of embargoed countries ip_country = self._country_code_from_ip(ip_addr) if ip_country in self._embargoed_countries: return self.REASONS['ip_country'].format( ip_addr=ip_addr, ip_country=ip_country, from_course=self._from_course_msg(course_id, course_is_embargoed) )
def test_add_valid_ips(self): # test adding valid ip addresses # should be able to do both ipv4 and ipv6 # spacing should not matter form_data = { 'whitelist': '127.0.0.1, 2003:dead:beef:4dad:23:46:bb:101', 'blacklist': ' 18.244.1.5 , 2002:c0a8:101::42, 18.36.22.1' } form = IPFilterForm(data=form_data) self.assertTrue(form.is_valid()) form.save() whitelist = IPFilter.current().whitelist_ips blacklist = IPFilter.current().blacklist_ips for addr in '127.0.0.1, 2003:dead:beef:4dad:23:46:bb:101'.split(','): self.assertIn(addr.strip(), whitelist) for addr in '18.244.1.5, 2002:c0a8:101::42, 18.36.22.1'.split(','): self.assertIn(addr.strip(), blacklist) # Test clearing by adding an empty list is OK too form_data = {'whitelist': '', 'blacklist': ''} form = IPFilterForm(data=form_data) self.assertTrue(form.is_valid()) form.save() self.assertTrue(len(IPFilter.current().whitelist) == 0) self.assertTrue(len(IPFilter.current().blacklist) == 0)
def test_add_valid_ips(self): # test adding valid ip addresses # should be able to do both ipv4 and ipv6 # spacing should not matter form_data = { "whitelist": "127.0.0.1, 2003:dead:beef:4dad:23:46:bb:101", "blacklist": " 18.244.1.5 , 2002:c0a8:101::42, 18.36.22.1", } form = IPFilterForm(data=form_data) self.assertTrue(form.is_valid()) form.save() whitelist = IPFilter.current().whitelist_ips blacklist = IPFilter.current().blacklist_ips for addr in "127.0.0.1, 2003:dead:beef:4dad:23:46:bb:101".split(","): self.assertIn(addr.strip(), whitelist) for addr in "18.244.1.5, 2002:c0a8:101::42, 18.36.22.1".split(","): self.assertIn(addr.strip(), blacklist) # Test clearing by adding an empty list is OK too form_data = {"whitelist": "", "blacklist": ""} form = IPFilterForm(data=form_data) self.assertTrue(form.is_valid()) form.save() self.assertTrue(len(IPFilter.current().whitelist) == 0) self.assertTrue(len(IPFilter.current().blacklist) == 0)
def process_request(self, request): """ Processes embargo requests """ url = request.path course_id = course_id_from_url(url) # If they're trying to access a course that cares about embargoes if EmbargoedCourse.is_embargoed(course_id): # If we're having performance issues, add caching here ip_addr = get_ip(request) # if blacklisted, immediately fail if ip_addr in IPFilter.current().blacklist_ips: log.info( "Embargo: Restricting IP address %s to course %s because IP is blacklisted.", ip_addr, course_id) return redirect('embargo') country_code_from_ip = pygeoip.GeoIP( settings.GEOIP_PATH).country_code_by_addr(ip_addr) is_embargoed = country_code_from_ip in EmbargoedState.current( ).embargoed_countries_list # Fail if country is embargoed and the ip address isn't explicitly whitelisted if is_embargoed and ip_addr not in IPFilter.current( ).whitelist_ips: log.info( "Embargo: Restricting IP address %s to course %s because IP is from country %s.", ip_addr, course_id, country_code_from_ip) return redirect('embargo')
def process_request(self, request): """ Processes embargo requests """ url = request.path course_id = course_id_from_url(url) course_is_embargoed = EmbargoedCourse.is_embargoed(course_id) # If they're trying to access a course that cares about embargoes if self.site_enabled or course_is_embargoed: response = redirect('embargo') # Set the proper response if site is enabled if self.site_enabled: redirect_url = getattr(settings, 'EMBARGO_SITE_REDIRECT_URL', None) response = HttpResponseRedirect(redirect_url) if redirect_url \ else HttpResponseForbidden('Access Denied') # If we're having performance issues, add caching here ip_addr = get_ip(request) # if blacklisted, immediately fail if ip_addr in IPFilter.current().blacklist_ips: if course_is_embargoed: msg = "Embargo: Restricting IP address %s to course %s because IP is blacklisted." % \ (ip_addr, course_id) else: msg = "Embargo: Restricting IP address %s because IP is blacklisted." % ip_addr log.info(msg) return response # ipv6 support if ip_addr.find(':') >= 0: country_code_from_ip = pygeoip.GeoIP( settings.GEOIPV6_PATH).country_code_by_addr(ip_addr) else: country_code_from_ip = pygeoip.GeoIP( settings.GEOIP_PATH).country_code_by_addr(ip_addr) is_embargoed = country_code_from_ip in EmbargoedState.current( ).embargoed_countries_list # Fail if country is embargoed and the ip address isn't explicitly # whitelisted if is_embargoed and ip_addr not in IPFilter.current( ).whitelist_ips: if course_is_embargoed: msg = "Embargo: Restricting IP address %s to course %s because IP is from country %s." % \ (ip_addr, course_id, country_code_from_ip) else: msg = "Embargo: Restricting IP address %s because IP is from country %s." % \ (ip_addr, country_code_from_ip) log.info(msg) return response
def process_request(self, request): """ Processes embargo requests """ url = request.path course_id = course_id_from_url(url) course_is_embargoed = EmbargoedCourse.is_embargoed(course_id) # If they're trying to access a course that cares about embargoes if self.site_enabled or course_is_embargoed: response = redirect("embargo") # Set the proper response if site is enabled if self.site_enabled: redirect_url = getattr(settings, "EMBARGO_SITE_REDIRECT_URL", None) response = ( HttpResponseRedirect(redirect_url) if redirect_url else HttpResponseForbidden("Access Denied") ) # If we're having performance issues, add caching here ip_addr = get_ip(request) # if blacklisted, immediately fail if ip_addr in IPFilter.current().blacklist_ips: if course_is_embargoed: msg = "Embargo: Restricting IP address %s to course %s because IP is blacklisted." % ( ip_addr, course_id, ) else: msg = "Embargo: Restricting IP address %s because IP is blacklisted." % ip_addr log.info(msg) return response country_code_from_ip = pygeoip.GeoIP(settings.GEOIP_PATH).country_code_by_addr(ip_addr) is_embargoed = country_code_from_ip in EmbargoedState.current().embargoed_countries_list # Fail if country is embargoed and the ip address isn't explicitly whitelisted if is_embargoed and ip_addr not in IPFilter.current().whitelist_ips: if course_is_embargoed: msg = "Embargo: Restricting IP address %s to course %s because IP is from country %s." % ( ip_addr, course_id, country_code_from_ip, ) else: msg = "Embargo: Restricting IP address %s because IP is from country %s." % ( ip_addr, country_code_from_ip, ) log.info(msg) return response
def test_ip_network_blocking(self): whitelist = '1.0.0.0/24' blacklist = '1.1.0.0/16' IPFilter(whitelist=whitelist, blacklist=blacklist).save() cwhitelist = IPFilter.current().whitelist_ips self.assertTrue('1.0.0.100' in cwhitelist) self.assertTrue('1.0.0.10' in cwhitelist) self.assertFalse('1.0.1.0' in cwhitelist) cblacklist = IPFilter.current().blacklist_ips self.assertTrue('1.1.0.0' in cblacklist) self.assertTrue('1.1.0.1' in cblacklist) self.assertTrue('1.1.1.0' in cblacklist) self.assertFalse('1.2.0.0' in cblacklist)
def test_ip_blocking(self): whitelist = '127.0.0.1' blacklist = '18.244.51.3' cwhitelist = IPFilter.current().whitelist_ips self.assertFalse(whitelist in cwhitelist) cblacklist = IPFilter.current().blacklist_ips self.assertFalse(blacklist in cblacklist) IPFilter(whitelist=whitelist, blacklist=blacklist).save() cwhitelist = IPFilter.current().whitelist_ips self.assertTrue(whitelist in cwhitelist) cblacklist = IPFilter.current().blacklist_ips self.assertTrue(blacklist in cblacklist)
def test_ip_network_blocking(self): whitelist = '1.0.0.0/24' blacklist = '1.1.0.0/16' IPFilter(whitelist=whitelist, blacklist=blacklist).save() cwhitelist = IPFilter.current().whitelist_ips self.assertIn('1.0.0.100', cwhitelist) self.assertIn('1.0.0.10', cwhitelist) self.assertNotIn('1.0.1.0', cwhitelist) cblacklist = IPFilter.current().blacklist_ips self.assertIn('1.1.0.0', cblacklist) self.assertIn('1.1.0.1', cblacklist) self.assertIn('1.1.1.0', cblacklist) self.assertNotIn('1.2.0.0', cblacklist)
def test_ip_blocking(self): whitelist = '127.0.0.1' blacklist = '18.244.51.3' cwhitelist = IPFilter.current().whitelist_ips self.assertNotIn(whitelist, cwhitelist) cblacklist = IPFilter.current().blacklist_ips self.assertNotIn(blacklist, cblacklist) IPFilter(whitelist=whitelist, blacklist=blacklist).save() cwhitelist = IPFilter.current().whitelist_ips self.assertIn(whitelist, cwhitelist) cblacklist = IPFilter.current().blacklist_ips self.assertIn(blacklist, cblacklist)
def test_ip_exceptions(self): # Explicitly whitelist/blacklist some IPs IPFilter( whitelist='1.0.0.0', blacklist='5.0.0.0', changed_by=self.user, enabled=True ).save() # Accessing an embargoed page from a blocked IP that's been whitelisted # should succeed response = self.client.get(self.embargoed_page, HTTP_X_FORWARDED_FOR='1.0.0.0', REMOTE_ADDR='1.0.0.0') self.assertEqual(response.status_code, 200) # Accessing a regular course from a blocked IP that's been whitelisted should succeed response = self.client.get(self.regular_page, HTTP_X_FORWARDED_FOR='1.0.0.0', REMOTE_ADDR='1.0.0.0') self.assertEqual(response.status_code, 200) # Accessing an embargoed course from non-embargoed IP that's been blacklisted # should cause a redirect response = self.client.get(self.embargoed_page, HTTP_X_FORWARDED_FOR='5.0.0.0', REMOTE_ADDR='5.0.0.0') self.assertEqual(response.status_code, 302) # Following the redirect should give us the embargo page response = self.client.get( self.embargoed_page, HTTP_X_FORWARDED_FOR='5.0.0.0', REMOTE_ADDR='1.0.0.0', follow=True ) self.assertIn(self.embargo_text, response.content) # Accessing a regular course from a non-embargoed IP that's been blacklisted should succeed response = self.client.get(self.regular_page, HTTP_X_FORWARDED_FOR='5.0.0.0', REMOTE_ADDR='5.0.0.0') self.assertEqual(response.status_code, 200)
def test_countries_embargo_off(self): # When the middleware is turned off, all requests should go through # Accessing an embargoed page from a blocked IP OK response = self.client.get(self.embargoed_page, HTTP_X_FORWARDED_FOR='1.0.0.0', REMOTE_ADDR='1.0.0.0') self.assertEqual(response.status_code, 200) # Accessing a regular page from a blocked IP should succeed response = self.client.get(self.regular_page, HTTP_X_FORWARDED_FOR='1.0.0.0', REMOTE_ADDR='1.0.0.0') self.assertEqual(response.status_code, 200) # Explicitly whitelist/blacklist some IPs IPFilter( whitelist='1.0.0.0', blacklist='5.0.0.0', changed_by=self.user, enabled=True ).save() # Accessing an embargoed course from non-embargoed IP that's been blacklisted # should be OK response = self.client.get(self.embargoed_page, HTTP_X_FORWARDED_FOR='5.0.0.0', REMOTE_ADDR='5.0.0.0') self.assertEqual(response.status_code, 200) # Accessing a regular course from a non-embargoed IP that's been blacklisted should succeed response = self.client.get(self.regular_page, HTTP_X_FORWARDED_FOR='5.0.0.0', REMOTE_ADDR='5.0.0.0') self.assertEqual(response.status_code, 200)
def process_request(self, request): """Block requests based on embargo rules. This will perform the following checks: 1) If the user's IP address is blacklisted, block. 2) If the user's IP address is whitelisted, allow. 3) If the user's country (inferred from their IP address) is blocked for a courseware page, block. 4) If the user's country (retrieved from the user's profile) is blocked for a courseware page, block. 5) Allow access. """ # Never block certain patterns by IP address for pattern in self.ALLOW_URL_PATTERNS: if pattern.match(request.path) is not None: return None ip_address = get_ip(request) ip_filter = IPFilter.current() if ip_filter.enabled and ip_address in ip_filter.blacklist_ips: log.info( ( u"User %s was blocked from accessing %s " u"because IP address %s is blacklisted." ), request.user.id, request.path, ip_address ) # If the IP is blacklisted, reject. # This applies to any request, not just courseware URLs. ip_blacklist_url = reverse( 'embargo_blocked_message', kwargs={ 'access_point': 'courseware', 'message_key': 'embargo' } ) return redirect(ip_blacklist_url) elif ip_filter.enabled and ip_address in ip_filter.whitelist_ips: log.info( ( u"User %s was allowed access to %s because " u"IP address %s is whitelisted." ), request.user.id, request.path, ip_address ) # If the IP is whitelisted, then allow access, # skipping later checks. return None else: # Otherwise, perform the country access checks. # This applies only to courseware URLs. return self.country_access_rules(request.user, ip_address, request.path)
def test_add_valid_ips(self): # test adding valid ip addresses # should be able to do both ipv4 and ipv6 # spacing should not matter form_data = { 'whitelist': '127.0.0.1, 2003:dead:beef:4dad:23:46:bb:101, 1.1.0.1/32, 1.0.0.0/24', 'blacklist': ' 18.244.1.5 , 2002:c0a8:101::42, 18.36.22.1, 1.0.0.0/16' } form = IPFilterForm(data=form_data) self.assertTrue(form.is_valid()) form.save() whitelist = IPFilter.current().whitelist_ips blacklist = IPFilter.current().blacklist_ips for addr in '127.0.0.1, 2003:dead:beef:4dad:23:46:bb:101'.split(','): self.assertIn(addr.strip(), whitelist) for addr in '18.244.1.5, 2002:c0a8:101::42, 18.36.22.1'.split(','): self.assertIn(addr.strip(), blacklist) # Network tests # ips not in whitelist network for addr in ['1.1.0.2', '1.0.1.0']: self.assertNotIn(addr.strip(), whitelist) # ips in whitelist network for addr in ['1.1.0.1', '1.0.0.100']: self.assertIn(addr.strip(), whitelist) # ips not in blacklist network for addr in ['2.0.0.0', '1.1.0.0']: self.assertNotIn(addr.strip(), blacklist) # ips in blacklist network for addr in ['1.0.100.0', '1.0.0.10']: self.assertIn(addr.strip(), blacklist) # Test clearing by adding an empty list is OK too form_data = { 'whitelist': '', 'blacklist': '' } form = IPFilterForm(data=form_data) self.assertTrue(form.is_valid()) form.save() self.assertEqual(len(IPFilter.current().whitelist), 0) self.assertEqual(len(IPFilter.current().blacklist), 0)
def process_request(self, request): """Block requests based on embargo rules. This will perform the following checks: 1) If the user's IP address is blacklisted, block. 2) If the user's IP address is whitelisted, allow. 3) If the user's country (inferred from their IP address) is blocked for a courseware page, block. 4) If the user's country (retrieved from the user's profile) is blocked for a courseware page, block. 5) Allow access. """ # Never block certain patterns by IP address for pattern in self.ALLOW_URL_PATTERNS: if pattern.match(request.path) is not None: return None ip_address = get_ip(request) ip_filter = IPFilter.current() if ip_filter.enabled and ip_address in ip_filter.blacklist_ips: log.info((u"User %s was blocked from accessing %s " u"because IP address %s is blacklisted."), request.user.id, request.path, ip_address) # If the IP is blacklisted, reject. # This applies to any request, not just courseware URLs. ip_blacklist_url = reverse('embargo_blocked_message', kwargs={ 'access_point': 'courseware', 'message_key': 'embargo' }) return redirect(ip_blacklist_url) elif ip_filter.enabled and ip_address in ip_filter.whitelist_ips: log.info((u"User %s was allowed access to %s because " u"IP address %s is whitelisted."), request.user.id, request.path, ip_address) # If the IP is whitelisted, then allow access, # skipping later checks. return None else: # Otherwise, perform the country access checks. # This applies only to courseware URLs. return self.country_access_rules(request.user, ip_address, request.path)
def process_request(self, request): """Block requests based on embargo rules. In the new ENABLE_COUNTRY_ACCESS implmentation, this will perform the following checks: 1) If the user's IP address is blacklisted, block. 2) If the user's IP address is whitelisted, allow. 3) If the user's country (inferred from their IP address) is blocked for a courseware page, block. 4) If the user's country (retrieved from the user's profile) is blocked for a courseware page, block. 5) Allow access. """ # If the feature flag is set, use the new "country access" implementation. # This is a more flexible implementation of the embargo feature that allows # per-course country access rules. if self.enable_country_access: # Never block certain patterns by IP address for pattern in self.ALLOW_URL_PATTERNS: if pattern.match(request.path) is not None: return None ip_address = get_ip(request) ip_filter = IPFilter.current() if ip_filter.enabled and ip_address in ip_filter.blacklist_ips: log.info( ( u"User %s was blocked from accessing %s " u"because IP address %s is blacklisted." ), request.user.id, request.path, ip_address ) # If the IP is blacklisted, reject. # This applies to any request, not just courseware URLs. ip_blacklist_url = reverse( 'embargo_blocked_message', kwargs={ 'access_point': 'courseware', 'message_key': 'embargo' } ) return redirect(ip_blacklist_url) elif ip_filter.enabled and ip_address in ip_filter.whitelist_ips: log.info( ( u"User %s was allowed access to %s because " u"IP address %s is whitelisted." ), request.user.id, request.path, ip_address ) # If the IP is whitelisted, then allow access, # skipping later checks. return None else: # Otherwise, perform the country access checks. # This applies only to courseware URLs. return self.country_access_rules(request.user, ip_address, request.path) url = request.path course_id = course_id_from_url(url) course_is_embargoed = EmbargoedCourse.is_embargoed(course_id) # If they're trying to access a course that cares about embargoes if self.site_enabled or course_is_embargoed: # Construct the list of functions that check whether the user is embargoed. # We wrap each of these functions in a decorator that logs the reason the user # was blocked. # Each function should return `True` iff the user is blocked by an embargo. check_functions = [ self._log_embargo_reason(check_func, course_id, course_is_embargoed) for check_func in [ partial(self._is_embargoed_by_ip, get_ip(request)), partial(self._is_embargoed_by_profile_country, request.user) ] ] # Perform each of the checks # If the user fails any of the checks, immediately redirect them # and skip later checks. for check_func in check_functions: if check_func(): return self._embargo_redirect_response
def process_request(self, request): """Block requests based on embargo rules. In the new ENABLE_COUNTRY_ACCESS implmentation, this will perform the following checks: 1) If the user's IP address is blacklisted, block. 2) If the user's IP address is whitelisted, allow. 3) If the user's country (inferred from their IP address) is blocked for a courseware page, block. 4) If the user's country (retrieved from the user's profile) is blocked for a courseware page, block. 5) Allow access. """ # If the feature flag is set, use the new "country access" implementation. # This is a more flexible implementation of the embargo feature that allows # per-course country access rules. if self.enable_country_access: # Never block certain patterns by IP address for pattern in self.ALLOW_URL_PATTERNS: if pattern.match(request.path) is not None: return None ip_address = get_ip(request) ip_filter = IPFilter.current() if ip_filter.enabled and ip_address in ip_filter.blacklist_ips: log.info((u"User %s was blocked from accessing %s " u"because IP address %s is blacklisted."), request.user.id, request.path, ip_address) # If the IP is blacklisted, reject. # This applies to any request, not just courseware URLs. ip_blacklist_url = reverse('embargo_blocked_message', kwargs={ 'access_point': 'courseware', 'message_key': 'embargo' }) return redirect(ip_blacklist_url) elif ip_filter.enabled and ip_address in ip_filter.whitelist_ips: log.info((u"User %s was allowed access to %s because " u"IP address %s is whitelisted."), request.user.id, request.path, ip_address) # If the IP is whitelisted, then allow access, # skipping later checks. return None else: # Otherwise, perform the country access checks. # This applies only to courseware URLs. return self.country_access_rules(request.user, ip_address, request.path) url = request.path course_id = course_id_from_url(url) course_is_embargoed = EmbargoedCourse.is_embargoed(course_id) # If they're trying to access a course that cares about embargoes if self.site_enabled or course_is_embargoed: # Construct the list of functions that check whether the user is embargoed. # We wrap each of these functions in a decorator that logs the reason the user # was blocked. # Each function should return `True` iff the user is blocked by an embargo. check_functions = [ self._log_embargo_reason(check_func, course_id, course_is_embargoed) for check_func in [ partial(self._is_embargoed_by_ip, get_ip(request)), partial(self._is_embargoed_by_profile_country, request.user) ] ] # Perform each of the checks # If the user fails any of the checks, immediately redirect them # and skip later checks. for check_func in check_functions: if check_func(): return self._embargo_redirect_response