Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lms/djangoapps/courseware/block_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
from openedx.core.djangoapps.credit.services import CreditService
from openedx.core.djangoapps.discussions.services import DiscussionConfigService
from openedx.core.djangoapps.enrollments.services import EnrollmentsService
from openedx.core.djangoapps.geoinfo.api import country_code_for_request
from openedx.core.djangoapps.util.user_utils import SystemUser
from openedx.core.djangoapps.video_config.services import VideoConfigService
from openedx.core.djangolib.markup import HTML
Expand Down Expand Up @@ -409,7 +410,7 @@ def get_block_for_descriptor(
"""
if request:
track_function = track_function or make_track_function(request)
user_location = user_location or getattr(request, 'session', {}).get('country_code')
user_location = user_location or country_code_for_request(request) or None
request_token = request_token or xblock_request_token(request)

if not student_data:
Expand Down
1 change: 0 additions & 1 deletion lms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1254,7 +1254,6 @@
'openedx.core.djangoapps.cors_csrf.middleware.CorsCSRFMiddleware',
'openedx.core.djangoapps.cors_csrf.middleware.CsrfCrossDomainCookieMiddleware',

'openedx.core.djangoapps.geoinfo.middleware.CountryMiddleware',
'openedx.core.djangoapps.embargo.middleware.EmbargoMiddleware',

# Allows us to use enterprise customer's language as the learner's default language
Expand Down
4 changes: 2 additions & 2 deletions openedx/core/djangoapps/catalog/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,9 +397,9 @@ def test_localized_string(self, mock_get_currency_data):
mock_get_currency_data.return_value = currency_data

request = RequestFactory().get("/dummy-url")
request.session = {"country_code": "CA"}
expected_result = "$20 CAD"
assert get_localized_price_text(10, request) == expected_result
with mock.patch(UTILS_MODULE + ".country_code_for_request", return_value="CA"):
assert get_localized_price_text(10, request) == expected_result


@skip_unless_lms
Expand Down
4 changes: 2 additions & 2 deletions openedx/core/djangoapps/catalog/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
SITE_PROGRAM_UUIDS_CACHE_KEY_TPL,
)
from openedx.core.djangoapps.catalog.models import CatalogIntegration
from openedx.core.djangoapps.geoinfo.api import country_code_for_request
from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_for_user
from openedx.core.lib.edx_api_utils import get_api_data

Expand Down Expand Up @@ -366,8 +367,7 @@ def get_localized_price_text(price, request):
"""
user_currency = {"symbol": "$", "rate": 1, "code": "USD"}

# session.country_code is added via CountryMiddleware in the LMS
user_location = getattr(request, "session", {}).get("country_code")
user_location = country_code_for_request(request)

# Override default user_currency if location is available
if user_location and get_currency_data:
Expand Down
23 changes: 23 additions & 0 deletions openedx/core/djangoapps/geoinfo/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

import geoip2.database
from django.conf import settings
from python_ipware import IpWare

_REQUEST_COUNTRY_CODE_ATTR = '_geoinfo_country_code'


def country_code_from_ip(ip_addr: str) -> str:
Expand All @@ -27,3 +30,23 @@ def country_code_from_ip(ip_addr: str) -> str:
country_code = ""
reader.close()
return country_code


def country_code_for_request(request) -> str:
"""
Return the country code for the client IP address of a request.

The lookup runs at most once per request and is not stored in the session,
so requests that don't otherwise use the session don't create or modify one.

Returns:
A 2-letter country code, or an empty string if the client IP is missing,
not globally routable, or not found.
"""
if not hasattr(request, _REQUEST_COUNTRY_CODE_ATTR):
ip_address, _ = IpWare().get_client_ip(meta=request.META)
country_code = ""
if ip_address and ip_address.is_global:
country_code = country_code_from_ip(format(ip_address))
setattr(request, _REQUEST_COUNTRY_CODE_ATTR, country_code)
return getattr(request, _REQUEST_COUNTRY_CODE_ATTR)
47 changes: 0 additions & 47 deletions openedx/core/djangoapps/geoinfo/middleware.py

This file was deleted.

77 changes: 77 additions & 0 deletions openedx/core/djangoapps/geoinfo/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""
Tests for the geoinfo API.
"""


from unittest.mock import MagicMock, PropertyMock, patch

import ddt
import geoip2
import maxminddb
from django.contrib.sessions.middleware import SessionMiddleware
from django.test import TestCase
from django.test.client import RequestFactory

from openedx.core.djangoapps.geoinfo.api import country_code_for_request


@ddt.ddt
class CountryCodeForRequestTests(TestCase):
"""
Tests of country_code_for_request.
"""
def setUp(self):
super().setUp()
self.request_factory = RequestFactory()
patcher = patch.object(maxminddb, 'open_database')
patcher.start()
self.country_mock = MagicMock(side_effect=self.mock_country)
country_patcher = patch.object(geoip2.database.Reader, 'country', self.country_mock)
country_patcher.start()
self.addCleanup(patcher.stop)
self.addCleanup(country_patcher.stop)

def mock_country(self, ip_address):
"""
Return a mock geoip2 country response for the given IP address.
"""
ip_dict = {
'117.79.83.1': 'CN',
'4.0.0.0': 'SD',
'2001:da8:20f:1502:edcf:550b:4a9c:207d': 'CN',
}

magic_mock = MagicMock()
magic_mock.country = MagicMock()
type(magic_mock.country).iso_code = PropertyMock(return_value=ip_dict.get(ip_address))

return magic_mock

@ddt.data(
('117.79.83.1', 'CN'),
('4.0.0.0', 'SD'),
('2001:da8:20f:1502:edcf:550b:4a9c:207d', 'CN'),
('8.8.8.8', ''),
)
@ddt.unpack
def test_country_code(self, ip_address, expected_country_code):
request = self.request_factory.get('/somewhere', HTTP_X_FORWARDED_FOR=ip_address)
assert country_code_for_request(request) == expected_country_code

def test_non_global_ip_address_is_not_looked_up(self):
request = self.request_factory.get('/somewhere', HTTP_X_FORWARDED_FOR='10.0.0.1')
assert country_code_for_request(request) == ''
self.country_mock.assert_not_called()

def test_lookup_runs_once_per_request(self):
request = self.request_factory.get('/somewhere', HTTP_X_FORWARDED_FOR='117.79.83.1')
assert country_code_for_request(request) == 'CN'
assert country_code_for_request(request) == 'CN'
self.country_mock.assert_called_once_with('117.79.83.1')

def test_session_is_not_modified(self):
request = self.request_factory.get('/somewhere', HTTP_X_FORWARDED_FOR='117.79.83.1')
SessionMiddleware(get_response=lambda request: None).process_request(request)
assert country_code_for_request(request) == 'CN'
assert not request.session.modified
assert request.session.is_empty()
124 changes: 0 additions & 124 deletions openedx/core/djangoapps/geoinfo/tests/test_middleware.py

This file was deleted.

1 change: 0 additions & 1 deletion xmodule/video_block/video_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,6 @@ def get_html(self, view=STUDENT_VIEW, context=None): # pylint: disable=argument
# If the user comes from China use China CDN for html5 videos.
# 'CN' is China ISO 3166-1 country code.
# Video caching is disabled for Studio. User_location is always None in Studio.
# CountryMiddleware disabled for Studio.
if getattr(self, 'video_speed_optimizations', True) and cdn_url:

if self.edx_video_id and edxval_api and video_status != 'external':
Expand Down
Loading