diff --git a/cms/static/js/views/export.js b/cms/static/js/views/export.js index 73007363b9..18f1cc182a 100644 --- a/cms/static/js/views/export.js +++ b/cms/static/js/views/export.js @@ -191,7 +191,7 @@ define([ * @return {JSON} the data of the previous export */ storedExport: function(contentHomeUrl) { - var storedData = JSON.parse($.cookie(COOKIE_NAME)); + var storedData = JSON.parse($.cookie(COOKIE_NAME) || null); if (storedData) { successUnixDate = storedData.date; } diff --git a/common/djangoapps/student/signals/receivers.py b/common/djangoapps/student/signals/receivers.py index 6d0cf4f061..079647c145 100644 --- a/common/djangoapps/student/signals/receivers.py +++ b/common/djangoapps/student/signals/receivers.py @@ -23,6 +23,7 @@ from common.djangoapps.student.models import ( ) from common.djangoapps.student.models_api import confirm_name_change from common.djangoapps.student.signals import USER_EMAIL_CHANGED +from openedx.core.djangoapps.safe_sessions.middleware import EmailChangeMiddleware from openedx.features.name_affirmation_api.utils import is_name_affirmation_installed logger = logging.getLogger(__name__) @@ -105,8 +106,12 @@ if is_name_affirmation_installed(): @receiver(USER_EMAIL_CHANGED) -def _listen_for_user_email_changed(sender, user, **kwargs): - """ If user has changed their email, update that in email Braze. """ +def _listen_for_user_email_changed(sender, user, request, **kwargs): + """ If user has changed their email, update that in session and Braze profile. """ + + # Store the user's email for session consistency (used by EmailChangeMiddleware) + EmailChangeMiddleware.register_email_change(request, user.email) + email = user.email user_id = user.id attributes = [{'email': email, 'external_id': user_id}] diff --git a/common/djangoapps/student/tests/test_receivers.py b/common/djangoapps/student/tests/test_receivers.py index 8ce869a731..e987120a9d 100644 --- a/common/djangoapps/student/tests/test_receivers.py +++ b/common/djangoapps/student/tests/test_receivers.py @@ -9,7 +9,7 @@ from common.djangoapps.student.models import CourseEnrollmentCelebration, Pendin from common.djangoapps.student.signals.signals import USER_EMAIL_CHANGED from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory, UserProfileFactory from lms.djangoapps.courseware.toggles import COURSEWARE_MICROFRONTEND_PROGRESS_MILESTONES -from openedx.core.djangolib.testing.utils import skip_unless_lms +from openedx.core.djangolib.testing.utils import skip_unless_lms, get_mock_request from openedx.features.name_affirmation_api.utils import is_name_affirmation_installed from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order @@ -75,10 +75,18 @@ class ReceiversTest(SharedModuleStoreTestCase): @patch('common.djangoapps.student.signals.receivers.get_braze_client') def test_listen_for_user_email_changed(self, mock_get_braze_client): """ - Ensure that USER_EMAIL_CHANGED signal triggers correct calls to get_braze_client. + Ensure that USER_EMAIL_CHANGED signal triggers correct calls to + get_braze_client and update email in session. """ user = UserFactory(email='email@test.com', username='jdoe') + request = get_mock_request(user=user) + request.session = self.client.session - USER_EMAIL_CHANGED.send(sender=None, user=user) + # simulating email change + user.email = 'new_email@test.com' + user.save() + + USER_EMAIL_CHANGED.send(sender=None, user=user, request=request) assert mock_get_braze_client.called + assert request.session.get('email', None) == user.email diff --git a/common/djangoapps/student/views/management.py b/common/djangoapps/student/views/management.py index cb3df1627f..d3e1c54180 100644 --- a/common/djangoapps/student/views/management.py +++ b/common/djangoapps/student/views/management.py @@ -910,7 +910,7 @@ def confirm_email_change(request, key): response = render_to_response("email_change_successful.html", address_context) - USER_EMAIL_CHANGED.send(sender=None, user=user) + USER_EMAIL_CHANGED.send(sender=None, user=user, request=request) return response diff --git a/lms/djangoapps/certificates/apis/v0/tests/test_views.py b/lms/djangoapps/certificates/apis/v0/tests/test_views.py index 9e62eab6d8..fd31e34569 100644 --- a/lms/djangoapps/certificates/apis/v0/tests/test_views.py +++ b/lms/djangoapps/certificates/apis/v0/tests/test_views.py @@ -309,7 +309,7 @@ class CertificatesListRestApiTest(AuthAndScopesTestMixin, SharedModuleStoreTestC def test_query_counts(self): # Test student with no certificates student_no_cert = UserFactory.create(password=self.user_password) - with self.assertNumQueries(17, table_ignorelist=WAFFLE_TABLES): + with self.assertNumQueries(21, table_ignorelist=WAFFLE_TABLES): resp = self.get_response( AuthType.jwt, requesting_user=self.global_staff, @@ -319,7 +319,7 @@ class CertificatesListRestApiTest(AuthAndScopesTestMixin, SharedModuleStoreTestC assert len(resp.data) == 0 # Test student with 1 certificate - with self.assertNumQueries(12, table_ignorelist=WAFFLE_TABLES): + with self.assertNumQueries(13, table_ignorelist=WAFFLE_TABLES): resp = self.get_response( AuthType.jwt, requesting_user=self.global_staff, @@ -359,7 +359,7 @@ class CertificatesListRestApiTest(AuthAndScopesTestMixin, SharedModuleStoreTestC download_url='www.google.com', grade="0.88", ) - with self.assertNumQueries(12, table_ignorelist=WAFFLE_TABLES): + with self.assertNumQueries(13, table_ignorelist=WAFFLE_TABLES): resp = self.get_response( AuthType.jwt, requesting_user=self.global_staff, diff --git a/lms/djangoapps/course_api/tests/test_views.py b/lms/djangoapps/course_api/tests/test_views.py index c39f808478..4065c54a98 100644 --- a/lms/djangoapps/course_api/tests/test_views.py +++ b/lms/djangoapps/course_api/tests/test_views.py @@ -434,7 +434,7 @@ class CourseListSearchViewTest(CourseApiTestViewMixin, ModuleStoreTestCase, Sear self.setup_user(self.audit_user) # These query counts were found empirically - query_counts = [50, 46, 46, 46, 46, 46, 46, 46, 46, 46, 16] + query_counts = [53, 46, 46, 46, 46, 46, 46, 46, 46, 46, 16] ordered_course_ids = sorted([str(cid) for cid in (course_ids + [c.id for c in self.courses])]) self.clear_caches() diff --git a/lms/djangoapps/course_home_api/course_metadata/tests/test_views.py b/lms/djangoapps/course_home_api/course_metadata/tests/test_views.py index 74b66fa5f0..4b3524eb1e 100644 --- a/lms/djangoapps/course_home_api/course_metadata/tests/test_views.py +++ b/lms/djangoapps/course_home_api/course_metadata/tests/test_views.py @@ -86,6 +86,7 @@ class CourseHomeMetadataTests(BaseCourseHomeTests): assert self.client.get(self.url).data['username'] == self.user.username def test_get_unknown_course(self): + self.client.logout() url = reverse('course-home:course-metadata', args=['course-v1:unknown+course+2T2020']) # Django TestCase wraps every test in a transaction, so we must specifically wrap this when we expect an error with transaction.atomic(): diff --git a/lms/djangoapps/discussion/rest_api/discussions_notifications.py b/lms/djangoapps/discussion/rest_api/discussions_notifications.py index 31f158fbd7..cba7852023 100644 --- a/lms/djangoapps/discussion/rest_api/discussions_notifications.py +++ b/lms/djangoapps/discussion/rest_api/discussions_notifications.py @@ -1,8 +1,6 @@ """ Discussion notifications sender util. """ -import logging - from django.conf import settings from lms.djangoapps.discussion.django_comment_client.permissions import get_team from openedx_events.learning.data import UserNotificationData, CourseNotificationData @@ -22,9 +20,6 @@ from openedx.core.djangoapps.django_comment_common.models import ( ) -log = logging.getLogger(__name__) - - class DiscussionNotificationSender: """ Class to send notifications to users who are subscribed to the thread. @@ -262,8 +257,6 @@ class DiscussionNotificationSender: 'username': self.creator.username, 'post_title': self.thread.title } - - log.info(f"Temp: Audience filter for course-wide notification is {audience_filters}") self._send_course_wide_notification(notification_type, audience_filters, context) diff --git a/lms/envs/common.py b/lms/envs/common.py index 94d298cddb..b567b20a37 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -2238,6 +2238,9 @@ MIDDLEWARE = [ #'django.contrib.auth.middleware.AuthenticationMiddleware', 'openedx.core.djangoapps.cache_toolbox.middleware.CacheBackedAuthenticationMiddleware', + # Middleware to flush user's session in other browsers when their email is changed. + 'openedx.core.djangoapps.safe_sessions.middleware.EmailChangeMiddleware', + 'common.djangoapps.student.middleware.UserStandingMiddleware', 'openedx.core.djangoapps.contentserver.middleware.StaticContentServer', @@ -5041,6 +5044,20 @@ HIBP_LOGIN_BLOCK_PASSWORD_FREQUENCY_THRESHOLD = 5 # .. toggle_tickets: https://openedx.atlassian.net/browse/VAN-838 ENABLE_DYNAMIC_REGISTRATION_FIELDS = False +############## Settings for EmailChangeMiddleware ############### + +# .. toggle_name: ENFORCE_SESSION_EMAIL_MATCH +# .. toggle_implementation: DjangoSetting +# .. toggle_default: False +# .. toggle_description: When enabled, this setting invalidates sessions in other browsers +# upon email change, while preserving the session validity in the browser where the +# email change occurs. This toggle is just being used for rollout. +# .. toggle_use_cases: temporary +# .. toggle_creation_date: 2023-12-07 +# .. toggle_target_removal_date: 2024-04-01 +# .. toggle_tickets: https://2u-internal.atlassian.net/browse/VAN-1797 +ENFORCE_SESSION_EMAIL_MATCH = False + LEARNER_HOME_MFE_REDIRECT_PERCENTAGE = 0 ############### Settings for the ace_common plugin ################# diff --git a/openedx/core/djangoapps/notifications/audience_filters.py b/openedx/core/djangoapps/notifications/audience_filters.py index 8d02bb3ea7..fe9a047f78 100644 --- a/openedx/core/djangoapps/notifications/audience_filters.py +++ b/openedx/core/djangoapps/notifications/audience_filters.py @@ -1,7 +1,6 @@ """ Audience based filters for notifications """ -import logging from abc import abstractmethod @@ -22,9 +21,6 @@ from openedx.core.djangoapps.django_comment_common.models import ( ) -log = logging.getLogger(__name__) - - class NotificationAudienceFilterBase: """ Base class for notification audience filters @@ -84,12 +80,10 @@ class CourseRoleAudienceFilter(NotificationAudienceFilterBase): if 'staff' in course_roles: staff_users = CourseStaffRole(course_key).users_with_role().values_list('id', flat=True) - log.info(f'Temp: Course wide notification, staff users calculated are {staff_users}') user_ids.extend(staff_users) if 'instructor' in course_roles: instructor_users = CourseInstructorRole(course_key).users_with_role().values_list('id', flat=True) - log.info(f'Temp: Course wide notification, instructor users calculated are {instructor_users}') user_ids.extend(instructor_users) return user_ids diff --git a/openedx/core/djangoapps/notifications/handlers.py b/openedx/core/djangoapps/notifications/handlers.py index 6f8b4775b9..dcd5f1ec1e 100644 --- a/openedx/core/djangoapps/notifications/handlers.py +++ b/openedx/core/djangoapps/notifications/handlers.py @@ -96,13 +96,10 @@ def calculate_course_wide_notification_audience(course_key, audience_filters): if filter_class: filter_instance = filter_class(course_key) filtered_users = filter_instance.filter(filter_values) - log.info(f'Temp: Course-wide notification filtered users are ' - f'{filtered_users} for filter type {filter_type}') audience_user_ids.extend(filtered_users) else: raise ValueError(f"Invalid audience filter type: {filter_type}") - log.info(f'Temp: Course-wide notification after audience filter is applied, users: {list(set(audience_user_ids))}') return list(set(audience_user_ids)) @@ -131,5 +128,4 @@ def generate_course_notifications(signal, sender, course_notification_data, meta 'content_url': course_notification_data.get('content_url'), } - log.info(f"Temp: Course-wide notification, user_ids to sent notifications to {notification_data.get('user_ids')}") send_notifications.delay(**notification_data) diff --git a/openedx/core/djangoapps/safe_sessions/middleware.py b/openedx/core/djangoapps/safe_sessions/middleware.py index 5f4449d93c..f3948217ef 100644 --- a/openedx/core/djangoapps/safe_sessions/middleware.py +++ b/openedx/core/djangoapps/safe_sessions/middleware.py @@ -95,7 +95,7 @@ from edx_django_utils.logging import encrypt_for_log from edx_django_utils.monitoring import set_custom_attribute from edx_toggles.toggles import SettingToggle -from openedx.core.djangoapps.user_authn.cookies import delete_logged_in_cookies +from openedx.core.djangoapps.user_authn.cookies import delete_logged_in_cookies, set_logged_in_cookies from openedx.core.lib.mobile_utils import is_request_from_mobile_app # .. toggle_name: LOG_REQUEST_USER_CHANGES @@ -768,6 +768,92 @@ class SafeSessionMiddleware(SessionMiddleware, MiddlewareMixin): return encrypt_for_log(str(request.headers), getattr(settings, 'SAFE_SESSIONS_DEBUG_PUBLIC_KEY', None)) +class EmailChangeMiddleware(MiddlewareMixin): + """ + Middleware responsible for performing the following + jobs on detecting an email change + 1) It will update the session's email and update the JWT cookie + to match the new email. + 2) It will invalidate any future session on other browsers where + the user's email does not match its session email. + + This middleware ensures that the sessions in other browsers + are invalidated when a user changes their email in one browser. + The active session in which the email change is made will remain valid. + + The user's email is stored in their session and JWT cookies during login + and gets updated when the user changes their email. + This middleware checks for any mismatch between the stored email + and the current user's email in each request, and if found, + it invalidates/flushes the session and mark cookies for deletion in request + which are then deleted in the process_response of SafeSessionMiddleware. + """ + + def process_request(self, request): + """ + Invalidate the user session if there's a mismatch + between the email in the user's session and request.user.email. + """ + if request.user.is_authenticated: + user_session_email = request.session.get('email', None) + are_emails_mismatched = user_session_email is not None and request.user.email != user_session_email + EmailChangeMiddleware._set_session_email_match_custom_attributes(are_emails_mismatched) + if settings.ENFORCE_SESSION_EMAIL_MATCH and are_emails_mismatched: + # Flush the session and mark cookies for deletion. + log.info( + f'EmailChangeMiddleware invalidating session for user: {request.user.id} due to email mismatch.' + ) + request.session.flush() + request.user = AnonymousUser() + _mark_cookie_for_deletion(request) + + def process_response(self, request, response): + """ + 1. Update the logged-in cookies if the email change was requested + 2. Store user's email in session if not already + """ + if request.user.is_authenticated: + if request.session.get('email', None) is None: + # .. custom_attribute_name: session_with_no_email_found + # .. custom_attribute_description: Indicates that user's email was not + # yet stored in the user's session. + set_custom_attribute('session_with_no_email_found', True) + request.session['email'] = request.user.email + + if request_cache.get_cached_response('email_change_requested').is_found: + # Update the JWT cookies with new user email + response = set_logged_in_cookies(request, response, request.user) + + return response + + @staticmethod + def register_email_change(request, email): + """ + Stores the fact that an email change happened. + + 1. Sets the email in session for later comparison. + 2. Sets a request level variable to mark that the user email change was requested. + """ + request.session['email'] = email + request_cache.set('email_change_requested', True) + + @staticmethod + def _set_session_email_match_custom_attributes(are_emails_mismatched): + """ + Sets custom attributes of session_email_match + """ + # .. custom_attribute_name: session_email_match + # .. custom_attribute_description: Indicates whether there is a match between the + # email in the user's session and the current user's email in the request. + set_custom_attribute('session_email_mismatch', are_emails_mismatched) + + # .. custom_attribute_name: is_enforce_session_email_match_enabled + # .. custom_attribute_description: Indicates whether session email match was enforced. + # When enforced/enabled, it invalidates sessions in other browsers upon email change, + # while preserving the session validity in the browser where the email change occurs. + set_custom_attribute('is_enforce_session_email_match_enabled', settings.ENFORCE_SESSION_EMAIL_MATCH) + + def obscure_token(value: Union[str, None]) -> Union[str, None]: """ Return a short string that can be used to detect other occurrences diff --git a/openedx/core/djangoapps/safe_sessions/tests/test_middleware.py b/openedx/core/djangoapps/safe_sessions/tests/test_middleware.py index 079cdcdd0c..0ffb1aeb8e 100644 --- a/openedx/core/djangoapps/safe_sessions/tests/test_middleware.py +++ b/openedx/core/djangoapps/safe_sessions/tests/test_middleware.py @@ -1,22 +1,29 @@ """ Unit tests for SafeSessionMiddleware """ - +import uuid from unittest.mock import call, patch, MagicMock import ddt from crum import set_current_request from django.conf import settings from django.contrib.auth import SESSION_KEY -from django.contrib.auth.models import AnonymousUser +from django.contrib.auth.models import AnonymousUser, User # lint-amnesty, pylint: disable=imported-auth-user from django.http import HttpResponse, HttpResponseRedirect, SimpleCookie from django.test import TestCase from django.test.utils import override_settings +from django.urls import reverse +from edx_django_utils.cache import RequestCache +from edx_rest_framework_extensions.auth.jwt import cookies as jwt_cookies -from openedx.core.djangolib.testing.utils import get_mock_request, CacheIsolationTestCase +from common.djangoapps.student.models import PendingEmailChange +from openedx.core.djangolib.testing.utils import get_mock_request, CacheIsolationTestCase, skip_unless_lms +from openedx.core.djangoapps.user_authn.tests.utils import setup_login_oauth_client +from openedx.core.djangoapps.user_authn.cookies import ALL_LOGGED_IN_COOKIE_NAMES from common.djangoapps.student.tests.factories import UserFactory from ..middleware import ( + EmailChangeMiddleware, SafeCookieData, SafeSessionMiddleware, mark_user_change_as_expected, @@ -615,3 +622,748 @@ class TestTrackRequestUserChanges(TestCase): request.user = object() assert len(request.debug_user_changes) == 2 assert "Changing request user but user has no id." in request.debug_user_changes[1] + + +@skip_unless_lms +class TestEmailChangeMiddleware(TestSafeSessionsLogMixin, TestCase): + """ + Test class for EmailChangeMiddleware + """ + + def setUp(self): + super().setUp() + self.EMAIL = 'test@example.com' + self.PASSWORD = 'Password1234' + self.user = UserFactory.create(email=self.EMAIL, password=self.PASSWORD) + self.addCleanup(set_current_request, None) + self.request = get_mock_request(self.user) + self.request.session = {} + self.client.response = HttpResponse() + self.client.response.cookies = SimpleCookie() + self.addCleanup(RequestCache.clear_all_namespaces) + + self.login_url = reverse("user_api_login_session", kwargs={'api_version': 'v2'}) + self.register_url = reverse("user_api_registration_v2") + self.dashboard_url = reverse('dashboard') + + @override_settings(ENFORCE_SESSION_EMAIL_MATCH=False) + @patch('openedx.core.djangoapps.safe_sessions.middleware._mark_cookie_for_deletion') + def test_process_request_user_not_authenticated_with_toggle_disabled(self, mock_mark_cookie_for_deletion): + """ + Calls EmailChangeMiddleware.process_request when no user is authenticated + and ENFORCE_SESSION_EMAIL_MATCH toggle is disabled. + Verifies that session and cookies are not affected. + """ + # Unauthenticated User + self.request.user = AnonymousUser() + + # Call process_request without authenticating a user + EmailChangeMiddleware(get_response=lambda request: None).process_request(self.request) + + # Assert that session and cookies are not affected + # Assert that _mark_cookie_for_deletion not called + mock_mark_cookie_for_deletion.assert_not_called() + + @override_settings(ENFORCE_SESSION_EMAIL_MATCH=True) + @patch('openedx.core.djangoapps.safe_sessions.middleware._mark_cookie_for_deletion') + def test_process_request_user_not_authenticated_with_toggle_enabled(self, mock_mark_cookie_for_deletion): + """ + Calls EmailChangeMiddleware.process_request when no user is authenticated + and ENFORCE_SESSION_EMAIL_MATCH toggle is enabled. + Verifies that session and cookies are not affected. + """ + # Unauthenticated User + self.request.user = AnonymousUser() + + # Call process_request without authenticating a user + EmailChangeMiddleware(get_response=lambda request: None).process_request(self.request) + + # Assert that session and cookies are not affected + # Assert that _mark_cookie_for_deletion not called + mock_mark_cookie_for_deletion.assert_not_called() + + @override_settings(ENFORCE_SESSION_EMAIL_MATCH=True) + @patch('openedx.core.djangoapps.safe_sessions.middleware._mark_cookie_for_deletion') + @patch("openedx.core.djangoapps.safe_sessions.middleware.set_custom_attribute") + def test_process_request_emails_match_with_toggle_enabled( + self, mock_set_custom_attribute, mock_mark_cookie_for_deletion + ): + """ + Calls EmailChangeMiddleware.process_request when user is authenticated, + ENFORCE_SESSION_EMAIL_MATCH is enabled and user session and request email also match. + Verifies that session and cookies are not affected. + """ + # Log in the user + self.client.login(email=self.user.email, password=self.PASSWORD) + self.request.session = self.client.session + self.client.response.set_cookie(settings.SESSION_COOKIE_NAME, 'authenticated') # Add some logged-in cookie + + # Registering email change (store user's email in session for later comparison by + # process_request function of middleware) + EmailChangeMiddleware.register_email_change(request=self.request, email=self.user.email) + + # Ensure email is set in the session + self.assertEqual(self.request.session.get('email'), self.user.email) + # Ensure session cookie exist + self.assertEqual(len(self.client.response.cookies), 1) + + # No email change occurred in any browser + + # Call process_request + EmailChangeMiddleware(get_response=lambda request: None).process_request(self.request) + + # Verify that session_email_mismatch and is_enforce_session_email_match_enabled + # custom attributes are set + mock_set_custom_attribute.assert_has_calls([call('session_email_mismatch', False)]) + mock_set_custom_attribute.assert_has_calls([call('is_enforce_session_email_match_enabled', True)]) + + # Assert that the session and cookies are not affected + self.assertEqual(self.request.session.get('email'), self.user.email) + self.assertEqual(len(self.client.response.cookies), 1) + self.assertEqual(self.client.response.cookies[settings.SESSION_COOKIE_NAME].value, 'authenticated') + + # Assert that _mark_cookie_for_deletion not called + mock_mark_cookie_for_deletion.assert_not_called() + + @override_settings(ENFORCE_SESSION_EMAIL_MATCH=False) + @patch('openedx.core.djangoapps.safe_sessions.middleware._mark_cookie_for_deletion') + @patch("openedx.core.djangoapps.safe_sessions.middleware.set_custom_attribute") + def test_process_request_emails_match_with_toggle_disabled( + self, mock_set_custom_attribute, mock_mark_cookie_for_deletion + ): + """ + Calls EmailChangeMiddleware.process_request when user is authenticated, + ENFORCE_SESSION_EMAIL_MATCH is disabled and user session and request email match. + Verifies that session and cookies are not affected. + """ + # Log in the user + self.client.login(email=self.user.email, password=self.PASSWORD) + self.request.session = self.client.session + self.client.response.set_cookie(settings.SESSION_COOKIE_NAME, 'authenticated') # Add some logged-in cookie + + # Registering email change (store user's email in session for later comparison by + # process_request function of middleware) + EmailChangeMiddleware.register_email_change(request=self.request, email=self.user.email) + + # Ensure email is set in the session + self.assertEqual(self.request.session.get('email'), self.user.email) + # Ensure session cookie exist + self.assertEqual(len(self.client.response.cookies), 1) + + # No email change occurred in any browser + + # Call process_request + EmailChangeMiddleware(get_response=lambda request: None).process_request(self.request) + + # Verify that session_email_mismatch and is_enforce_session_email_match_enabled + # custom attributes are set + mock_set_custom_attribute.assert_has_calls([call('session_email_mismatch', False)]) + mock_set_custom_attribute.assert_has_calls([call('is_enforce_session_email_match_enabled', False)]) + + # Assert that the session and cookies are not affected + self.assertEqual(self.request.session.get('email'), self.user.email) + self.assertEqual(len(self.client.response.cookies), 1) + self.assertEqual(self.client.response.cookies[settings.SESSION_COOKIE_NAME].value, 'authenticated') + + # Assert that _mark_cookie_for_deletion not called + mock_mark_cookie_for_deletion.assert_not_called() + + @override_settings(ENFORCE_SESSION_EMAIL_MATCH=True) + @patch('openedx.core.djangoapps.safe_sessions.middleware._mark_cookie_for_deletion') + @patch("openedx.core.djangoapps.safe_sessions.middleware.set_custom_attribute") + def test_process_request_emails_mismatch_with_toggle_enabled( + self, mock_set_custom_attribute, mock_mark_cookie_for_deletion + ): + """ + Calls EmailChangeMiddleware.process_request when user is authenticated, + ENFORCE_SESSION_EMAIL_MATCH is enabled and user session and request + email mismatch. (Email was changed in some other browser) + Verifies that session is flushed and cookies are marked for deletion. + """ + # Log in the user + self.client.login(email=self.user.email, password=self.PASSWORD) + self.request.session = self.client.session + self.client.response.set_cookie(settings.SESSION_COOKIE_NAME, 'authenticated') # Add some logged-in cookie + + # Registering email change (store user's email in session for later comparison by + # process_request function of middleware) + EmailChangeMiddleware.register_email_change(request=self.request, email=self.user.email) + + # Ensure email is set in the session + self.assertEqual(self.request.session.get('email'), self.user.email) + # Ensure session cookie exist + self.assertEqual(len(self.client.response.cookies), 1) + + # simulating email changed in some other browser + self.user.email = 'new_email@test.com' + self.user.save() + + # Call process_request + EmailChangeMiddleware(get_response=lambda request: None).process_request(self.request) + + # Verify that session_email_mismatch and is_enforce_session_email_match_enabled + # custom attributes are set + mock_set_custom_attribute.assert_has_calls([call('session_email_mismatch', True)]) + mock_set_custom_attribute.assert_has_calls([call('is_enforce_session_email_match_enabled', True)]) + + # Assert that the session is flushed and cookies marked for deletion + mock_mark_cookie_for_deletion.assert_called() + assert self.request.session.get(SESSION_KEY) is None + assert self.request.user == AnonymousUser() + + @override_settings(ENFORCE_SESSION_EMAIL_MATCH=False) + @patch('openedx.core.djangoapps.safe_sessions.middleware._mark_cookie_for_deletion') + @patch("openedx.core.djangoapps.safe_sessions.middleware.set_custom_attribute") + def test_process_request_emails_mismatch_with_toggle_disabled( + self, mock_set_custom_attribute, mock_mark_cookie_for_deletion + ): + """ + Calls EmailChangeMiddleware.process_request when user is authenticated, + ENFORCE_SESSION_EMAIL_MATCH is disabled and user session and request + email mismatch. (Email was changed in some other browser) + Verifies that session and cookies are not affected. + """ + # Log in the user + self.client.login(email=self.user.email, password=self.PASSWORD) + self.request.session = self.client.session + self.client.response.set_cookie(settings.SESSION_COOKIE_NAME, 'authenticated') # Add some logged-in cookie + + # Registering email change (store user's email in session for later comparison by + # process_request function of middleware) + EmailChangeMiddleware.register_email_change(request=self.request, email=self.user.email) + + # Ensure email is set in the session + self.assertEqual(self.request.session.get('email'), self.user.email) + # Ensure session cookie exist + self.assertEqual(len(self.client.response.cookies), 1) + + # simulating email changed in some other browser + self.user.email = 'new_email@test.com' + self.user.save() + + # Call process_request + EmailChangeMiddleware(get_response=lambda request: None).process_request(self.request) + + # Verify that session_email_mismatch and is_enforce_session_email_match_enabled + # custom attributes are set + mock_set_custom_attribute.assert_has_calls([call('session_email_mismatch', True)]) + mock_set_custom_attribute.assert_has_calls([call('is_enforce_session_email_match_enabled', False)]) + + # Assert that the session and cookies are not affected + self.assertNotEqual(self.request.session.get('email'), self.user.email) + self.assertEqual(len(self.client.response.cookies), 1) + self.assertEqual(self.client.response.cookies[settings.SESSION_COOKIE_NAME].value, 'authenticated') + + # Assert that _mark_cookie_for_deletion not called + mock_mark_cookie_for_deletion.assert_not_called() + + @override_settings(ENFORCE_SESSION_EMAIL_MATCH=True) + @patch('openedx.core.djangoapps.safe_sessions.middleware._mark_cookie_for_deletion') + def test_process_request_no_email_change_history_with_toggle_enabled( + self, mock_mark_cookie_for_deletion + ): + """ + Calls EmailChangeMiddleware.process_request when there is no previous + history of an email change and ENFORCE_SESSION_EMAIL_MATCH is enabled + Verifies that existing sessions are not affected. + Test that sessions predating this code are not affected. + """ + # Log in the user (Simulating user logged-in before this code and email was not set in session) + self.client.login(email=self.user.email, password=self.PASSWORD) + self.request.session = self.client.session + self.client.response.set_cookie(settings.SESSION_COOKIE_NAME, 'authenticated') # Add some logged-in cookie + + # Ensure there is no email in the session denoting no previous history of email change + self.assertEqual(self.request.session.get('email'), None) + + # Ensure session cookie exist + self.assertEqual(len(self.client.response.cookies), 1) + + # simulating email changed in some other browser + self.user.email = 'new_email@test.com' + self.user.save() + + # Call process_request + EmailChangeMiddleware(get_response=lambda request: None).process_request(self.request) + + # Assert that the session and cookies are not affected + self.assertEqual(len(self.client.response.cookies), 1) + self.assertEqual(self.client.response.cookies[settings.SESSION_COOKIE_NAME].value, 'authenticated') + + # Assert that _mark_cookie_for_deletion not called + mock_mark_cookie_for_deletion.assert_not_called() + + @override_settings(ENFORCE_SESSION_EMAIL_MATCH=False) + @patch('openedx.core.djangoapps.safe_sessions.middleware._mark_cookie_for_deletion') + def test_process_request_no_email_change_history_with_toggle_disabled( + self, mock_mark_cookie_for_deletion + ): + """ + Calls EmailChangeMiddleware.process_request when there is no previous + history of an email change and ENFORCE_SESSION_EMAIL_MATCH is disabled + Verifies that existing sessions are not affected. + Test that sessions predating this code are not affected. + """ + # Log in the user (Simulating user logged-in before this code and email was not set in session) + self.client.login(email=self.user.email, password=self.PASSWORD) + self.request.session = self.client.session + self.client.response.set_cookie(settings.SESSION_COOKIE_NAME, 'authenticated') # Add some logged-in cookie + + # Ensure there is no email in the session denoting no previous history of email change + self.assertEqual(self.request.session.get('email'), None) + + # Ensure session cookie exist + self.assertEqual(len(self.client.response.cookies), 1) + + # simulating email changed in some other browser + self.user.email = 'new_email@test.com' + self.user.save() + + # Call process_request + EmailChangeMiddleware(get_response=lambda request: None).process_request(self.request) + + # Assert that the session and cookies are not affected + self.assertEqual(len(self.client.response.cookies), 1) + self.assertEqual(self.client.response.cookies[settings.SESSION_COOKIE_NAME].value, 'authenticated') + + # Assert that _mark_cookie_for_deletion not called + mock_mark_cookie_for_deletion.assert_not_called() + + @patch("openedx.core.djangoapps.safe_sessions.middleware.set_logged_in_cookies") + def test_process_response_user_not_authenticated(self, mock_set_logged_in_cookies): + """ + Calls EmailChangeMiddleware.process_response when user is not authenticated. + Verify that the logged-in cookies are not updated + """ + # return value of mock + mock_set_logged_in_cookies.return_value = self.client.response + + # Unauthenticated User + self.request.user = AnonymousUser() + + # Call process_response without authenticating a user + response = EmailChangeMiddleware(get_response=lambda request: None).process_response( + self.request, self.client.response + ) + + assert response.status_code == 200 + # Assert that cookies are not updated + # Assert that mock_set_logged_in_cookies not called + mock_set_logged_in_cookies.assert_not_called() + + @patch("openedx.core.djangoapps.safe_sessions.middleware.set_logged_in_cookies") + def test_process_response_user_authenticated_but_email_change_not_requested(self, mock_set_logged_in_cookies): + """ + Calls EmailChangeMiddleware.process_response when user is authenticated but email + change was not requested. + Verify that the logged-in cookies are not updated + """ + # return value of mock + mock_set_logged_in_cookies.return_value = self.client.response + + # Log in the user + self.client.login(email=self.user.email, password=self.PASSWORD) + self.request.session = self.client.session + self.client.response.set_cookie(settings.SESSION_COOKIE_NAME, 'authenticated') # Add some logged-in cookie + + # No call to register_email_change to indicate email was not changed + + # Call process_response + response = EmailChangeMiddleware(get_response=lambda request: None).process_response( + self.request, self.client.response + ) + + assert response.status_code == 200 + # Assert that cookies are not updated + # Assert that mock_set_logged_in_cookies not called + mock_set_logged_in_cookies.assert_not_called() + + @patch("openedx.core.djangoapps.safe_sessions.middleware.set_logged_in_cookies") + def test_process_response_user_authenticated_and_email_change_requested(self, mock_set_logged_in_cookies): + """ + Calls EmailChangeMiddleware.process_response when user is authenticated and email + change was requested. + Verify that the logged-in cookies are updated + """ + # return value of mock + mock_set_logged_in_cookies.return_value = self.client.response + + # Log in the user + self.client.login(email=self.user.email, password=self.PASSWORD) + self.request.session = self.client.session + self.client.response.set_cookie(settings.SESSION_COOKIE_NAME, 'authenticated') # Add some logged-in cookie + + # Registering email change (setting a variable `email_change_requested` to indicate email was changed) + # so that process_response can update cookies + EmailChangeMiddleware.register_email_change(request=self.request, email=self.user.email) + + # Call process_response + response = EmailChangeMiddleware(get_response=lambda request: None).process_response( + self.request, self.client.response + ) + + assert response.status_code == 200 + # Assert that cookies are updated + # Assert that mock_set_logged_in_cookies is called + mock_set_logged_in_cookies.assert_called() + + def test_process_response_no_email_in_session(self): + """ + Calls EmailChangeMiddleware.process_response when user is authenticated and + user's email was not stored in user's session. + Verify that the user's email is stored in session + """ + # Log in the user + self.client.login(email=self.user.email, password=self.PASSWORD) + self.request.session = self.client.session + self.client.response.set_cookie(settings.SESSION_COOKIE_NAME, 'authenticated') # Add some logged-in cookie + + # Ensure there is no email in the session + self.assertEqual(self.request.session.get('email'), None) + + # Call process_response + response = EmailChangeMiddleware(get_response=lambda request: None).process_response( + self.request, self.client.response + ) + + assert response.status_code == 200 + # Verify that email is set in the session + self.assertEqual(self.request.session.get('email'), self.user.email) + + @patch.dict("django.conf.settings.FEATURES", {"DISABLE_SET_JWT_COOKIES_FOR_TESTS": False}) + def test_user_remain_authenticated_on_email_change_in_other_browser_with_toggle_disabled(self): + """ + Integration Test: test that a user remains authenticated upon email change + in other browser when ENFORCE_SESSION_EMAIL_MATCH toggle is disabled + Verify that the session and cookies are not affected in current browser and + user remains authenticated + """ + setup_login_oauth_client() + + # Login the user with 'test@example.com` email and test password in current browser + response = self.client.post(self.login_url, { + "email_or_username": self.EMAIL, + "password": self.PASSWORD, + }) + # Verify that the user is logged in successfully in current browser + assert response.status_code == 200 + # Verify that the logged-in cookies are set in current browser + self._assert_logged_in_cookies_present(response) + + # Verify that the authenticated user can access the dashboard in current browser + response = self.client.get(self.dashboard_url) + assert response.status_code == 200 + + # simulating email changed in some other browser (Email is changed in DB) + self.user.email = 'new_email@test.com' + self.user.save() + + # Verify that the user remains authenticated in current browser and can access the dashboard + response = self.client.get(self.dashboard_url) + assert response.status_code == 200 + + @patch.dict("django.conf.settings.FEATURES", {"DISABLE_SET_JWT_COOKIES_FOR_TESTS": False}) + @override_settings(ENFORCE_SESSION_EMAIL_MATCH=True) + def test_cookies_are_updated_with_new_email_on_email_change_with_toggle_enabled(self): + """ + Integration Test: test that cookies are updated with new email upon email change + in current browser regardless of toggle setting + Verify that the cookies are updated in current browser and + user remains authenticated + """ + setup_login_oauth_client() + + # Login the user with 'test@example.com` email and test password in current browser + login_response = self.client.post(self.login_url, { + "email_or_username": self.EMAIL, + "password": self.PASSWORD, + }) + # Verify that the user is logged in successfully in current browser + assert login_response.status_code == 200 + # Verify that the logged-in cookies are set in current browser + self._assert_logged_in_cookies_present(login_response) + + # Verify that the authenticated user can access the dashboard in current browser + response = self.client.get(self.dashboard_url) + assert response.status_code == 200 + + # simulating email change in current browser + activation_key = uuid.uuid4().hex + PendingEmailChange.objects.update_or_create( + user=self.user, + defaults={ + 'new_email': 'new_email@test.com', + 'activation_key': activation_key, + } + ) + email_change_response = self.client.get( + reverse('confirm_email_change', kwargs={'key': activation_key}), + ) + + # Verify that email change is successful + assert email_change_response.status_code == 200 + self._assert_logged_in_cookies_present(email_change_response) + + # Verify that jwt cookies are updated with new email and + # not equal to old logged-in cookies in current browser + self.assertNotEqual( + login_response.cookies[jwt_cookies.jwt_cookie_header_payload_name()].value, + email_change_response.cookies[jwt_cookies.jwt_cookie_header_payload_name()].value + ) + self.assertNotEqual( + login_response.cookies[jwt_cookies.jwt_cookie_signature_name()].value, + email_change_response.cookies[jwt_cookies.jwt_cookie_signature_name()].value + ) + + @patch.dict("django.conf.settings.FEATURES", {"DISABLE_SET_JWT_COOKIES_FOR_TESTS": False}) + @override_settings(ENFORCE_SESSION_EMAIL_MATCH=False) + def test_cookies_are_updated_with_new_email_on_email_change_with_toggle_disabled(self): + """ + Integration Test: test that cookies are updated with new email upon email change + in current browser regardless of toggle setting + Verify that the cookies are updated in current browser and + user remains authenticated + """ + setup_login_oauth_client() + + # Login the user with 'test@example.com` email and test password in current browser + login_response = self.client.post(self.login_url, { + "email_or_username": self.EMAIL, + "password": self.PASSWORD, + }) + # Verify that the user is logged in successfully in current browser + assert login_response.status_code == 200 + # Verify that the logged-in cookies are set in current browser + self._assert_logged_in_cookies_present(login_response) + + # Verify that the authenticated user can access the dashboard in current browser + response = self.client.get(self.dashboard_url) + assert response.status_code == 200 + + # simulating email change in current browser + activation_key = uuid.uuid4().hex + PendingEmailChange.objects.update_or_create( + user=self.user, + defaults={ + 'new_email': 'new_email@test.com', + 'activation_key': activation_key, + } + ) + email_change_response = self.client.get( + reverse('confirm_email_change', kwargs={'key': activation_key}), + ) + + # Verify that email change is successful + assert email_change_response.status_code == 200 + self._assert_logged_in_cookies_present(email_change_response) + + # Verify that jwt cookies are updated with new email and + # not equal to old logged-in cookies in current browser + self.assertNotEqual( + login_response.cookies[jwt_cookies.jwt_cookie_header_payload_name()].value, + email_change_response.cookies[jwt_cookies.jwt_cookie_header_payload_name()].value + ) + self.assertNotEqual( + login_response.cookies[jwt_cookies.jwt_cookie_signature_name()].value, + email_change_response.cookies[jwt_cookies.jwt_cookie_signature_name()].value + ) + + @patch.dict("django.conf.settings.FEATURES", {"DISABLE_SET_JWT_COOKIES_FOR_TESTS": False}) + @override_settings(ENFORCE_SESSION_EMAIL_MATCH=True) + def test_logged_in_user_unauthenticated_on_email_change_in_other_browser(self): + """ + Integration Test: Test that a user logged-in in one browser gets unauthenticated + when the email is changed in some other browser and the request and session emails mismatch. + Verify that the session is invalidated and cookies are deleted in current browser + and user gets unauthenticated. + """ + setup_login_oauth_client() + + # Login the user with 'test@example.com` email and test password in current browser + response = self.client.post(self.login_url, { + "email_or_username": self.EMAIL, + "password": self.PASSWORD, + }) + # Verify that the user is logged in successfully in current browser + assert response.status_code == 200 + # Verify that the logged-in cookies are set in current browser + self._assert_logged_in_cookies_present(response) + + # Verify that the authenticated user can access the dashboard in current browser + response = self.client.get(self.dashboard_url) + assert response.status_code == 200 + + # simulating email changed in some other browser (Email is changed in DB) + self.user.email = 'new_email@test.com' + self.user.save() + + # Verify that the user gets unauthenticated in current browser and cannot access the dashboard + response = self.client.get(self.dashboard_url) + assert response.status_code == 302 + self._assert_logged_in_cookies_not_present(response) + + @patch.dict("django.conf.settings.FEATURES", {"DISABLE_SET_JWT_COOKIES_FOR_TESTS": False}) + @override_settings(ENFORCE_SESSION_EMAIL_MATCH=True) + def test_logged_in_user_remains_authenticated_on_email_change_in_same_browser(self): + """ + Integration Test: test that a user logged-in in some browser remains authenticated + when the email is changed in same browser. + Verify that the session and cookies are updated in current browser and + user remains authenticated + """ + setup_login_oauth_client() + + # Login the user with 'test@example.com` email and test password in current browser + response = self.client.post(self.login_url, { + "email_or_username": self.EMAIL, + "password": self.PASSWORD, + }) + # Verify that the user is logged in successfully in current browser + assert response.status_code == 200 + # Verify that the logged-in cookies are set in current browser + self._assert_logged_in_cookies_present(response) + + # Verify that the authenticated user can access the dashboard in current browser + response = self.client.get(self.dashboard_url) + assert response.status_code == 200 + + # simulating email change in current browser + activation_key = uuid.uuid4().hex + PendingEmailChange.objects.update_or_create( + user=self.user, + defaults={ + 'new_email': 'new_email@test.com', + 'activation_key': activation_key, + } + ) + email_change_response = self.client.get( + reverse('confirm_email_change', kwargs={'key': activation_key}), + ) + + # Verify that email change is successful and all logged-in + # cookies are set in current browser + assert email_change_response.status_code == 200 + self._assert_logged_in_cookies_present(email_change_response) + + # Verify that the user remains authenticated in current browser and can access the dashboard + response = self.client.get(self.dashboard_url) + assert response.status_code == 200 + + @patch.dict("django.conf.settings.FEATURES", {"DISABLE_SET_JWT_COOKIES_FOR_TESTS": False}) + @override_settings(ENFORCE_SESSION_EMAIL_MATCH=True) + def test_registered_user_unauthenticated_on_email_change_in_other_browser(self): + """ + Integration Test: Test that a user registered in one browser gets unauthenticated + when the email is changed in some other browser and the request and session emails mismatch. + Verify that the session is invalidated and cookies are deleted in current browser + and user gets unauthenticated + """ + setup_login_oauth_client() + + # Register the user with 'john_doe@example.com` email and test password in current browser + response = self.client.post(self.register_url, { + "email": 'john_doe@example.com', + "name": 'John Doe', + "username": 'john_doe', + "password": 'password', + "honor_code": "true", + }) + # Verify that the user is logged in successfully in current browser + assert response.status_code == 200 + # Verify that the logged-in cookies are set in current browser + self._assert_logged_in_cookies_present(response) + + # Verify that the authenticated user can access the dashboard in current browser + response = self.client.get(self.dashboard_url) + assert response.status_code == 200 + + # simulating email changed in some other browser (Email is changed in DB) + registered_user = User.objects.get(email='john_doe@example.com') + registered_user.email = 'new_email@test.com' + registered_user.save() + + # Verify that the user get unauthenticated in current browser and cannot access the dashboard + response = self.client.get(self.dashboard_url) + assert response.status_code == 302 + self._assert_logged_in_cookies_not_present(response) + + @patch.dict("django.conf.settings.FEATURES", {"DISABLE_SET_JWT_COOKIES_FOR_TESTS": False}) + @override_settings(ENFORCE_SESSION_EMAIL_MATCH=True) + def test_registered_user_remain_authenticated_on_email_change_in_same_browser(self): + """ + Integration Test: test that a user registered in one browser remains + authenticated in current browser when the email is changed in same browser. + Verify that the session and cookies updated and user remains + authenticated in current browser + """ + setup_login_oauth_client() + + # Register the user with 'john_doe@example.com` email and test password in current browser + response = self.client.post(self.register_url, { + "email": 'john_doe@example.com', + "name": 'John Doe', + "username": 'john_doe', + "password": 'password', + "honor_code": "true", + }) + # Verify that the user is logged in successfully in current browser + assert response.status_code == 200 + # Verify that the logged-in cookies are set in current browser + self._assert_logged_in_cookies_present(response) + + # Verify that the authenticated user can access the dashboard in current browser + response = self.client.get(self.dashboard_url) + assert response.status_code == 200 + + # getting newly created user + registered_user = User.objects.get(email='john_doe@example.com') + + # simulating email change in current browser + activation_key = uuid.uuid4().hex + PendingEmailChange.objects.update_or_create( + user=registered_user, + defaults={ + 'new_email': 'new_email@test.com', + 'activation_key': activation_key, + } + ) + email_change_response = self.client.get( + reverse('confirm_email_change', kwargs={'key': activation_key}), + ) + + # Verify that email change is successful and all logged-in + # cookies are updated with new email in current browser + assert email_change_response.status_code == 200 + self._assert_logged_in_cookies_present(email_change_response) + + # Verify that the user remains authenticated in current browser and can access the dashboard + response = self.client.get(self.dashboard_url) + assert response.status_code == 200 + + def _assert_logged_in_cookies_present(self, response): + """ + Helper function to verify that all logged-in cookies are available + and have valid values (not empty strings) + """ + all_cookies = ALL_LOGGED_IN_COOKIE_NAMES + (settings.SESSION_COOKIE_NAME,) + + for cookie in all_cookies: + # Check if the cookie is present in response.cookies.keys() + self.assertIn(cookie, response.cookies.keys()) + + # Assert that the value is not an empty string + self.assertNotEqual(response.cookies[cookie].value, "") + + def _assert_logged_in_cookies_not_present(self, response): + """ + Helper function to verify that all logged-in cookies are cleared + and have empty values + """ + all_cookies = ALL_LOGGED_IN_COOKIE_NAMES + (settings.SESSION_COOKIE_NAME,) + + for cookie in all_cookies: + # Check if the cookie is present in response.cookies.keys() + self.assertIn(cookie, response.cookies.keys()) + + # Assert that the value is not an empty string + self.assertEqual(response.cookies[cookie].value, "") diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_views.py b/openedx/core/djangoapps/user_api/accounts/tests/test_views.py index 546b8afacc..0496f45ae2 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_views.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_views.py @@ -232,7 +232,7 @@ class TestOwnUsernameAPI(FilteredQueryCountMixin, CacheIsolationTestCase, UserAP Test that a client (logged in) can get her own username. """ self.client.login(username=self.user.username, password=TEST_PASSWORD) - self._verify_get_own_username(16) + self._verify_get_own_username(19) def test_get_username_inactive(self): """ @@ -242,7 +242,7 @@ class TestOwnUsernameAPI(FilteredQueryCountMixin, CacheIsolationTestCase, UserAP self.client.login(username=self.user.username, password=TEST_PASSWORD) self.user.is_active = False self.user.save() - self._verify_get_own_username(16) + self._verify_get_own_username(19) def test_get_username_not_logged_in(self): """ @@ -358,7 +358,7 @@ class TestAccountsAPI(FilteredQueryCountMixin, CacheIsolationTestCase, UserAPITe """ ENABLED_CACHES = ['default'] - TOTAL_QUERY_COUNT = 24 + TOTAL_QUERY_COUNT = 27 FULL_RESPONSE_FIELD_COUNT = 29 def setUp(self): @@ -811,7 +811,7 @@ class TestAccountsAPI(FilteredQueryCountMixin, CacheIsolationTestCase, UserAPITe assert data['time_zone'] is None self.client.login(username=self.user.username, password=TEST_PASSWORD) - verify_get_own_information(self._get_num_queries(22)) + verify_get_own_information(self._get_num_queries(25)) # Now make sure that the user can get the same information, even if not active self.user.is_active = False @@ -831,7 +831,7 @@ class TestAccountsAPI(FilteredQueryCountMixin, CacheIsolationTestCase, UserAPITe legacy_profile.save() self.client.login(username=self.user.username, password=TEST_PASSWORD) - with self.assertNumQueries(self._get_num_queries(22), table_ignorelist=WAFFLE_TABLES): + with self.assertNumQueries(self._get_num_queries(25), table_ignorelist=WAFFLE_TABLES): response = self.send_get(self.client) for empty_field in ("level_of_education", "gender", "country", "state", "bio",): assert response.data[empty_field] is None diff --git a/requirements/constraints.txt b/requirements/constraints.txt index f0b2dd767f..7e3e48f88b 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -23,7 +23,7 @@ click>=8.0,<9.0 # The team that owns this package will manually bump this package rather than having it pulled in automatically. # This is to allow them to better control its deployment and to do it in a process that works better # for them. -edx-enterprise==4.10.9 +edx-enterprise==4.10.11 # django-oauth-toolkit version >=2.0.0 has breaking changes. More details # mentioned on this issue https://github.com/openedx/edx-platform/issues/32884 @@ -107,3 +107,13 @@ openedx-learning==0.4.4 # Open AI version 1.0.0 dropped support for openai.ChatCompletion which is currently in use in enterprise. openai<=0.28.1 + +# optimizely-sdk 5.0.0 is breaking following test with segmentation fault +# common/djangoapps/third_party_auth/tests/test_views.py::SAMLMetadataTest::test_secure_key_configuration +# needs to be fixed in the follow up issue +# https://github.com/openedx/edx-platform/issues/34103 +optimizely-sdk<5.0 + +# lxml 5.1.0 introduced a breaking change in unit test shards +# This constraint can probably be removed once lxml==5.1.1 is released on PyPI +lxml<5.0 diff --git a/requirements/edx-sandbox/py38.txt b/requirements/edx-sandbox/py38.txt index 61dbc724bc..50547e11f8 100644 --- a/requirements/edx-sandbox/py38.txt +++ b/requirements/edx-sandbox/py38.txt @@ -22,7 +22,7 @@ cryptography==38.0.4 # -r requirements/edx-sandbox/py38.in cycler==0.12.1 # via matplotlib -fonttools==4.46.0 +fonttools==4.47.2 # via matplotlib importlib-resources==6.1.1 # via matplotlib @@ -30,11 +30,12 @@ joblib==1.3.2 # via nltk kiwisolver==1.4.5 # via matplotlib -lxml==4.9.3 +lxml==4.9.4 # via + # -c requirements/edx-sandbox/../constraints.txt # -r requirements/edx-sandbox/py38.in # openedx-calc -markupsafe==2.1.3 +markupsafe==2.1.4 # via # chem # openedx-calc @@ -59,7 +60,7 @@ openedx-calc==3.0.1 # via -r requirements/edx-sandbox/py38.in packaging==23.2 # via matplotlib -pillow==10.1.0 +pillow==10.2.0 # via matplotlib pycparser==2.21 # via cffi @@ -71,9 +72,9 @@ pyparsing==3.1.1 # openedx-calc python-dateutil==2.8.2 # via matplotlib -random2==1.0.1 +random2==1.0.2 # via -r requirements/edx-sandbox/py38.in -regex==2023.10.3 +regex==2023.12.25 # via nltk scipy==1.7.3 # via diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 4731fcfed6..6d5a04100d 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -35,7 +35,7 @@ async-timeout==4.0.3 # via # aiohttp # redis -attrs==23.1.0 +attrs==23.2.0 # via # -r requirements/edx/kernel.in # aiohttp @@ -58,7 +58,7 @@ backports-zoneinfo[tzdata]==0.2.1 # celery # icalendar # kombu -beautifulsoup4==4.12.2 +beautifulsoup4==4.12.3 # via pynliner billiard==4.2.0 # via celery @@ -73,13 +73,13 @@ bleach[css]==6.1.0 # xblock-poll boto==2.49.0 # via -r requirements/edx/kernel.in -boto3==1.33.12 +boto3==1.34.28 # via # -r requirements/edx/kernel.in # django-ses # fs-s3fs # ora2 -botocore==1.33.12 +botocore==1.34.28 # via # -r requirements/edx/kernel.in # boto3 @@ -243,6 +243,7 @@ django==3.2.23 # openedx-learning # ora2 # super-csv + # xblock-google-drive # xss-utils django-appconf==1.0.6 # via django-statici18n @@ -283,12 +284,12 @@ django-filter==23.5 # edx-enterprise # lti-consumer-xblock # openedx-blockstore -django-ipware==6.0.2 +django-ipware==6.0.3 # via # -r requirements/edx/kernel.in # edx-enterprise # edx-proctoring -django-js-asset==2.1.0 +django-js-asset==2.2.0 # via django-mptt django-method-override==1.0.4 # via -r requirements/edx/kernel.in @@ -325,7 +326,7 @@ django-oauth-toolkit==1.7.1 # edx-enterprise django-object-actions==4.2.0 # via edx-enterprise -django-pipeline==2.1.0 +django-pipeline==3.0.0 # via -r requirements/edx/kernel.in django-ratelimit==4.1.0 # via -r requirements/edx/kernel.in @@ -398,9 +399,9 @@ done-xblock==2.2.0 # via -r requirements/edx/bundled.in drf-jwt==1.19.2 # via edx-drf-extensions -drf-nested-routers==0.93.4 +drf-nested-routers==0.93.5 # via openedx-blockstore -drf-spectacular==0.27.0 +drf-spectacular==0.27.1 # via -r requirements/edx/kernel.in drf-yasg==1.21.5 # via @@ -418,7 +419,7 @@ edx-auth-backends==4.2.0 # via # -r requirements/edx/kernel.in # openedx-blockstore -edx-braze-client==0.1.8 +edx-braze-client==0.2.1 # via # -r requirements/edx/bundled.in # edx-enterprise @@ -446,7 +447,7 @@ edx-django-release-util==1.3.0 # openedx-blockstore edx-django-sites-extensions==4.0.2 # via -r requirements/edx/kernel.in -edx-django-utils==5.9.0 +edx-django-utils==5.10.1 # via # -r requirements/edx/kernel.in # django-config-models @@ -462,7 +463,7 @@ edx-django-utils==5.9.0 # openedx-blockstore # ora2 # super-csv -edx-drf-extensions==10.0.0 +edx-drf-extensions==10.1.0 # via # -r requirements/edx/kernel.in # edx-completion @@ -474,7 +475,7 @@ edx-drf-extensions==10.0.0 # edx-when # edxval # openedx-learning -edx-enterprise==4.10.9 +edx-enterprise==4.10.11 # via # -c requirements/edx/../constraints.txt # -r requirements/edx/kernel.in @@ -519,7 +520,7 @@ edx-rest-api-client==5.6.1 # edx-proctoring edx-search==3.8.2 # via -r requirements/edx/kernel.in -edx-sga==0.23.0 +edx-sga==0.23.1 # via -r requirements/edx/bundled.in edx-submissions==3.6.0 # via @@ -560,11 +561,11 @@ event-tracking==2.2.0 # edx-completion # edx-proctoring # edx-search -fastavro==1.9.1 +fastavro==1.9.3 # via openedx-events filelock==3.13.1 # via snowflake-connector-python -frozenlist==1.4.0 +frozenlist==1.4.1 # via # aiohttp # aiosignal @@ -601,7 +602,7 @@ idna==3.6 # requests # snowflake-connector-python # yarl -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 # via markdown importlib-resources==5.13.0 # via @@ -620,7 +621,7 @@ isodate==0.6.1 # via python3-saml itypes==1.2.0 # via coreapi -jinja2==3.1.2 +jinja2==3.1.3 # via # code-annotations # coreschema @@ -641,17 +642,17 @@ jsonfield==3.1.0 # edx-submissions # lti-consumer-xblock # ora2 -jsonschema==4.20.0 +jsonschema==4.21.1 # via # drf-spectacular # optimizely-sdk -jsonschema-specifications==2023.11.2 +jsonschema-specifications==2023.12.1 # via jsonschema -jwcrypto==1.5.0 +jwcrypto==1.5.1 # via # django-oauth-toolkit # pylti1p3 -kombu==5.3.4 +kombu==5.3.5 # via celery laboratory==1.0.2 # via -r requirements/edx/kernel.in @@ -668,10 +669,11 @@ libsass==0.10.0 # -r requirements/edx/paver.txt loremipsum==1.0.5 # via ora2 -lti-consumer-xblock==9.8.1 +lti-consumer-xblock==9.8.3 # via -r requirements/edx/kernel.in -lxml==4.9.3 +lxml==4.9.4 # via + # -c requirements/edx/../constraints.txt # -r requirements/edx/kernel.in # edx-i18n-tools # edxval @@ -690,7 +692,6 @@ mako==1.3.0 # acid-xblock # lti-consumer-xblock # xblock - # xblock-google-drive # xblock-utils markdown==3.3.7 # via @@ -701,7 +702,7 @@ markdown==3.3.7 # xblock-poll markey==0.8 # via enmerkar-underscore -markupsafe==2.1.3 +markupsafe==2.1.4 # via # -r requirements/edx/paver.txt # chem @@ -709,7 +710,7 @@ markupsafe==2.1.3 # mako # openedx-calc # xblock -maxminddb==2.5.1 +maxminddb==2.5.2 # via geoip2 mock==5.1.0 # via -r requirements/edx/paver.txt @@ -725,11 +726,11 @@ multidict==6.0.4 # via # aiohttp # yarl -mysqlclient==2.2.0 +mysqlclient==2.2.1 # via # -r requirements/edx/kernel.in # openedx-blockstore -newrelic==9.3.0 +newrelic==9.6.0 # via # -r requirements/edx/bundled.in # edx-django-utils @@ -756,13 +757,13 @@ openai==0.28.1 # via # -c requirements/edx/../constraints.txt # edx-enterprise -openedx-atlas==0.5.0 +openedx-atlas==0.6.0 # via -r requirements/edx/kernel.in openedx-blockstore==1.4.0 # via -r requirements/edx/kernel.in openedx-calc==3.0.1 # via -r requirements/edx/kernel.in -openedx-django-pyfs==3.4.0 +openedx-django-pyfs==3.4.1 # via # lti-consumer-xblock # xblock @@ -786,8 +787,10 @@ openedx-learning==0.4.4 openedx-mongodbproxy==0.2.0 # via -r requirements/edx/kernel.in optimizely-sdk==4.1.1 - # via -r requirements/edx/bundled.in -ora2==6.0.29 + # via + # -c requirements/edx/../constraints.txt + # -r requirements/edx/bundled.in +ora2==6.0.30 # via -r requirements/edx/bundled.in packaging==23.2 # via @@ -818,7 +821,7 @@ pgpy==0.6.0 # via edx-enterprise piexif==1.1.3 # via -r requirements/edx/kernel.in -pillow==10.1.0 +pillow==10.2.0 # via # -r requirements/edx/kernel.in # edx-enterprise @@ -830,9 +833,9 @@ platformdirs==3.11.0 # via snowflake-connector-python polib==1.2.0 # via edx-i18n-tools -prompt-toolkit==3.0.42 +prompt-toolkit==3.0.43 # via click-repl -psutil==5.9.6 +psutil==5.9.8 # via # -r requirements/edx/paver.txt # edx-django-utils @@ -846,7 +849,7 @@ pycountry==23.12.11 # via -r requirements/edx/kernel.in pycparser==2.21 # via cffi -pycryptodomex==3.19.0 +pycryptodomex==3.20.0 # via # -r requirements/edx/kernel.in # edx-proctoring @@ -919,11 +922,11 @@ python-dateutil==2.8.2 # olxcleaner # ora2 # xblock -python-ipware==2.0.0 +python-ipware==2.0.1 # via django-ipware -python-memcached==1.59 +python-memcached==1.62 # via -r requirements/edx/paver.txt -python-slugify==8.0.1 +python-slugify==8.0.2 # via code-annotations python-swiftclient==4.4.0 # via ora2 @@ -965,19 +968,19 @@ pyyaml==6.0.1 # edx-django-release-util # edx-i18n-tools # xblock -random2==1.0.1 +random2==1.0.2 # via -r requirements/edx/kernel.in -recommender-xblock==2.0.1 +recommender-xblock==2.1.1 # via -r requirements/edx/bundled.in redis==5.0.1 # via # -r requirements/edx/kernel.in # walrus -referencing==0.32.0 +referencing==0.32.1 # via # jsonschema # jsonschema-specifications -regex==2023.10.3 +regex==2023.12.25 # via nltk requests==2.31.0 # via @@ -1002,11 +1005,12 @@ requests==2.31.0 # slumber # snowflake-connector-python # social-auth-core + # xblock-google-drive requests-oauthlib==1.3.1 # via # -r requirements/edx/kernel.in # social-auth-core -rpds-py==0.13.2 +rpds-py==0.17.1 # via # jsonschema # referencing @@ -1020,7 +1024,7 @@ rules==3.3 # edx-enterprise # edx-proctoring # openedx-learning -s3transfer==0.8.2 +s3transfer==0.10.0 # via boto3 sailthru-client==2.2.3 # via edx-ace @@ -1069,14 +1073,13 @@ six==1.16.0 # py2neo # pyjwkest # python-dateutil - # python-memcached slumber==0.7.1 # via # -r requirements/edx/kernel.in # edx-bulk-grades # edx-enterprise # edx-rest-api-client -snowflake-connector-python==3.6.0 +snowflake-connector-python==3.7.0 # via edx-enterprise social-auth-app-django==5.0.0 # via @@ -1141,7 +1144,7 @@ typing-extensions==4.9.0 # kombu # pylti1p3 # snowflake-connector-python -tzdata==2023.3 +tzdata==2023.4 # via # backports-zoneinfo # celery @@ -1176,7 +1179,7 @@ walrus==0.9.3 # via edx-event-bus-redis watchdog==3.0.0 # via -r requirements/edx/paver.txt -wcwidth==0.2.12 +wcwidth==0.2.13 # via prompt-toolkit web-fragments==2.1.0 # via @@ -1199,7 +1202,7 @@ wrapt==1.16.0 # via # -r requirements/edx/paver.txt # deprecated -xblock[django]==1.9.0 +xblock[django]==1.10.0 # via # -r requirements/edx/kernel.in # acid-xblock @@ -1215,16 +1218,14 @@ xblock[django]==1.9.0 # xblock-google-drive # xblock-poll # xblock-utils -xblock-drag-and-drop-v2==3.3.0 +xblock-drag-and-drop-v2==3.4.0 # via -r requirements/edx/bundled.in -xblock-google-drive==0.5.0 +xblock-google-drive==0.6.1 # via -r requirements/edx/bundled.in xblock-poll==1.13.0 # via -r requirements/edx/bundled.in xblock-utils==4.0.0 - # via - # edx-sga - # xblock-google-drive + # via edx-sga xmlsec==1.3.13 # via python3-saml xss-utils==0.5.0 diff --git a/requirements/edx/coverage.txt b/requirements/edx/coverage.txt index 6df26a54fd..3d8191aa4c 100644 --- a/requirements/edx/coverage.txt +++ b/requirements/edx/coverage.txt @@ -6,15 +6,15 @@ # chardet==5.2.0 # via diff-cover -coverage==7.3.2 +coverage==7.4.0 # via -r requirements/edx/coverage.in -diff-cover==8.0.1 +diff-cover==8.0.3 # via -r requirements/edx/coverage.in -jinja2==3.1.2 +jinja2==3.1.3 # via diff-cover -markupsafe==2.1.3 +markupsafe==2.1.4 # via jinja2 -pluggy==1.3.0 +pluggy==1.4.0 # via diff-cover pygments==2.17.2 # via diff-cover diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 50b40cf849..f04ae1ec5f 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -53,10 +53,9 @@ annotated-types==0.6.0 # via # -r requirements/edx/testing.txt # pydantic -anyio==3.7.1 +anyio==4.2.0 # via # -r requirements/edx/testing.txt - # fastapi # starlette appdirs==1.4.4 # via @@ -86,7 +85,7 @@ async-timeout==4.0.3 # -r requirements/edx/testing.txt # aiohttp # redis -attrs==23.1.0 +attrs==23.2.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -119,7 +118,7 @@ backports-zoneinfo[tzdata]==0.2.1 # celery # icalendar # kombu -beautifulsoup4==4.12.2 +beautifulsoup4==4.12.3 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -145,14 +144,14 @@ boto==2.49.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -boto3==1.33.12 +boto3==1.34.28 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # django-ses # fs-s3fs # ora2 -botocore==1.33.12 +botocore==1.34.28 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -281,7 +280,7 @@ coreschema==0.0.4 # -r requirements/edx/testing.txt # coreapi # drf-yasg -coverage[toml]==7.3.2 +coverage[toml]==7.4.0 # via # -r requirements/edx/testing.txt # coverage @@ -313,9 +312,9 @@ cssutils==2.9.0 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # pynliner -ddt==1.7.0 +ddt==1.7.1 # via -r requirements/edx/testing.txt -deepmerge==1.1.0 +deepmerge==1.1.1 # via # -r requirements/edx/doc.txt # sphinxcontrib-openapi @@ -332,7 +331,7 @@ deprecated==1.2.14 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # jwcrypto -diff-cover==8.0.1 +diff-cover==8.0.3 # via -r requirements/edx/testing.txt dill==0.3.7 # via @@ -416,6 +415,7 @@ django==3.2.23 # openedx-learning # ora2 # super-csv + # xblock-google-drive # xss-utils django-appconf==1.0.6 # via @@ -481,13 +481,13 @@ django-filter==23.5 # edx-enterprise # lti-consumer-xblock # openedx-blockstore -django-ipware==6.0.2 +django-ipware==6.0.3 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # edx-enterprise # edx-proctoring -django-js-asset==2.1.0 +django-js-asset==2.2.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -540,7 +540,7 @@ django-object-actions==4.2.0 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # edx-enterprise -django-pipeline==2.1.0 +django-pipeline==3.0.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -655,12 +655,12 @@ drf-jwt==1.19.2 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # edx-drf-extensions -drf-nested-routers==0.93.4 +drf-nested-routers==0.93.5 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # openedx-blockstore -drf-spectacular==0.27.0 +drf-spectacular==0.27.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -686,7 +686,7 @@ edx-auth-backends==4.2.0 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # openedx-blockstore -edx-braze-client==0.1.8 +edx-braze-client==0.2.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -725,7 +725,7 @@ edx-django-sites-extensions==4.0.2 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -edx-django-utils==5.9.0 +edx-django-utils==5.10.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -742,7 +742,7 @@ edx-django-utils==5.9.0 # openedx-blockstore # ora2 # super-csv -edx-drf-extensions==10.0.0 +edx-drf-extensions==10.1.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -755,7 +755,7 @@ edx-drf-extensions==10.0.0 # edx-when # edxval # openedx-learning -edx-enterprise==4.10.9 +edx-enterprise==4.10.11 # via # -c requirements/edx/../constraints.txt # -r requirements/edx/doc.txt @@ -824,7 +824,7 @@ edx-search==3.8.2 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -edx-sga==0.23.0 +edx-sga==0.23.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -896,15 +896,15 @@ execnet==2.0.2 # pytest-xdist factory-boy==3.3.0 # via -r requirements/edx/testing.txt -faker==20.1.0 +faker==22.5.1 # via # -r requirements/edx/testing.txt # factory-boy -fastapi==0.105.0 +fastapi==0.109.0 # via # -r requirements/edx/testing.txt # pact-python -fastavro==1.9.1 +fastavro==1.9.3 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -916,9 +916,9 @@ filelock==3.13.1 # snowflake-connector-python # tox # virtualenv -freezegun==1.3.1 +freezegun==1.4.0 # via -r requirements/edx/testing.txt -frozenlist==1.4.0 +frozenlist==1.4.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -949,13 +949,13 @@ gitdb==4.0.11 # via # -r requirements/edx/doc.txt # gitpython -gitpython==3.1.40 +gitpython==3.1.41 # via -r requirements/edx/doc.txt glob2==0.7 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -grimp==3.1 +grimp==3.2 # via # -r requirements/edx/testing.txt # import-linter @@ -995,9 +995,9 @@ imagesize==1.4.1 # via # -r requirements/edx/doc.txt # sphinx -import-linter==1.12.1 +import-linter==2.0 # via -r requirements/edx/testing.txt -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 # via # -r requirements/edx/../pip-tools.txt # -r requirements/edx/doc.txt @@ -1037,7 +1037,7 @@ isodate==0.6.1 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # python3-saml -isort==5.13.1 +isort==5.13.2 # via # -r requirements/edx/testing.txt # pylint @@ -1046,7 +1046,7 @@ itypes==1.2.0 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # coreapi -jinja2==3.1.2 +jinja2==3.1.3 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1080,25 +1080,25 @@ jsonfield==3.1.0 # edx-submissions # lti-consumer-xblock # ora2 -jsonschema==4.20.0 +jsonschema==4.21.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # drf-spectacular # optimizely-sdk # sphinxcontrib-openapi -jsonschema-specifications==2023.11.2 +jsonschema-specifications==2023.12.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # jsonschema -jwcrypto==1.5.0 +jwcrypto==1.5.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # django-oauth-toolkit # pylti1p3 -kombu==5.3.4 +kombu==5.3.5 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1115,7 +1115,7 @@ lazy==1.6 # lti-consumer-xblock # ora2 # xblock -lazy-object-proxy==1.9.0 +lazy-object-proxy==1.10.0 # via # -r requirements/edx/testing.txt # astroid @@ -1130,12 +1130,13 @@ loremipsum==1.0.5 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # ora2 -lti-consumer-xblock==9.8.1 +lti-consumer-xblock==9.8.3 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -lxml==4.9.3 +lxml==4.9.4 # via + # -c requirements/edx/../constraints.txt # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # edx-i18n-tools @@ -1159,7 +1160,6 @@ mako==1.3.0 # acid-xblock # lti-consumer-xblock # xblock - # xblock-google-drive # xblock-utils markdown==3.3.7 # via @@ -1174,7 +1174,7 @@ markey==0.8 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # enmerkar-underscore -markupsafe==2.1.3 +markupsafe==2.1.4 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1183,7 +1183,7 @@ markupsafe==2.1.3 # mako # openedx-calc # xblock -maxminddb==2.5.1 +maxminddb==2.5.2 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1221,19 +1221,19 @@ multidict==6.0.4 # -r requirements/edx/testing.txt # aiohttp # yarl -mypy==1.7.1 +mypy==1.8.0 # via # -r requirements/edx/development.in # django-stubs # djangorestframework-stubs mypy-extensions==1.0.0 # via mypy -mysqlclient==2.2.0 +mysqlclient==2.2.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # openedx-blockstore -newrelic==9.3.0 +newrelic==9.6.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1274,7 +1274,7 @@ openai==0.28.1 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # edx-enterprise -openedx-atlas==0.5.0 +openedx-atlas==0.6.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1286,7 +1286,7 @@ openedx-calc==3.0.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -openedx-django-pyfs==3.4.0 +openedx-django-pyfs==3.4.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1322,9 +1322,10 @@ openedx-mongodbproxy==0.2.0 # -r requirements/edx/testing.txt optimizely-sdk==4.1.1 # via + # -c requirements/edx/../constraints.txt # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -ora2==6.0.29 +ora2==6.0.30 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1385,7 +1386,7 @@ piexif==1.1.3 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -pillow==10.1.0 +pillow==10.2.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1407,7 +1408,7 @@ platformdirs==3.11.0 # snowflake-connector-python # tox # virtualenv -pluggy==1.3.0 +pluggy==1.4.0 # via # -r requirements/edx/testing.txt # diff-cover @@ -1418,12 +1419,12 @@ polib==1.2.0 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # edx-i18n-tools -prompt-toolkit==3.0.42 +prompt-toolkit==3.0.43 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # click-repl -psutil==5.9.6 +psutil==5.9.8 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1455,18 +1456,18 @@ pycparser==2.21 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # cffi -pycryptodomex==3.19.0 +pycryptodomex==3.20.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # edx-proctoring # lti-consumer-xblock # pyjwkest -pydantic==2.5.2 +pydantic==2.5.3 # via # -r requirements/edx/testing.txt # fastapi -pydantic-core==2.14.5 +pydantic-core==2.14.6 # via # -r requirements/edx/testing.txt # pydantic @@ -1591,7 +1592,7 @@ pysrt==1.1.2 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # edxval -pytest==7.4.3 +pytest==7.4.4 # via # -r requirements/edx/testing.txt # pylint-pytest @@ -1636,16 +1637,16 @@ python-dateutil==2.8.2 # olxcleaner # ora2 # xblock -python-ipware==2.0.0 +python-ipware==2.0.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # django-ipware -python-memcached==1.59 +python-memcached==1.62 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -python-slugify==8.0.1 +python-slugify==8.0.2 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1703,11 +1704,11 @@ pyyaml==6.0.1 # edx-i18n-tools # sphinxcontrib-openapi # xblock -random2==1.0.1 +random2==1.0.2 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -recommender-xblock==2.0.1 +recommender-xblock==2.1.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1716,13 +1717,13 @@ redis==5.0.1 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # walrus -referencing==0.32.0 +referencing==0.32.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # jsonschema # jsonschema-specifications -regex==2023.10.3 +regex==2023.12.25 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1754,12 +1755,13 @@ requests==2.31.0 # snowflake-connector-python # social-auth-core # sphinx + # xblock-google-drive requests-oauthlib==1.3.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # social-auth-core -rpds-py==0.13.2 +rpds-py==0.17.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1782,7 +1784,7 @@ rules==3.3 # edx-enterprise # edx-proctoring # openedx-learning -s3transfer==0.8.2 +s3transfer==0.10.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1850,7 +1852,6 @@ six==1.16.0 # py2neo # pyjwkest # python-dateutil - # python-memcached # sphinxcontrib-httpdomain slumber==0.7.1 # via @@ -1871,7 +1872,7 @@ snowballstemmer==2.2.0 # via # -r requirements/edx/doc.txt # sphinx -snowflake-connector-python==3.6.0 +snowflake-connector-python==3.7.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -1969,7 +1970,7 @@ staff-graded-xblock==2.2.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -starlette==0.27.0 +starlette==0.35.1 # via # -r requirements/edx/testing.txt # fastapi @@ -2007,8 +2008,6 @@ tinycss2==1.2.1 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # bleach -toml==0.10.2 - # via vulture tomli==2.0.1 # via # -r requirements/edx/../pip-tools.txt @@ -2024,6 +2023,7 @@ tomli==2.0.1 # pyproject-hooks # pytest # tox + # vulture tomlkit==0.12.3 # via # -r requirements/edx/doc.txt @@ -2053,6 +2053,7 @@ typing-extensions==4.9.0 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # annotated-types + # anyio # asgiref # astroid # django-countries @@ -2075,7 +2076,7 @@ typing-extensions==4.9.0 # snowflake-connector-python # starlette # uvicorn -tzdata==2023.3 +tzdata==2023.4 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -2109,7 +2110,7 @@ user-util==1.0.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -uvicorn==0.24.0.post1 +uvicorn==0.27.0 # via # -r requirements/edx/testing.txt # pact-python @@ -2129,7 +2130,7 @@ voluptuous==0.14.1 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # ora2 -vulture==2.10 +vulture==2.11 # via -r requirements/edx/development.in walrus==0.9.3 # via @@ -2141,7 +2142,7 @@ watchdog==3.0.0 # -r requirements/edx/development.in # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -wcwidth==0.2.12 +wcwidth==0.2.13 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -2177,7 +2178,7 @@ wrapt==1.16.0 # -r requirements/edx/testing.txt # astroid # deprecated -xblock[django]==1.9.0 +xblock[django]==1.10.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -2195,11 +2196,11 @@ xblock[django]==1.9.0 # xblock-google-drive # xblock-poll # xblock-utils -xblock-drag-and-drop-v2==3.3.0 +xblock-drag-and-drop-v2==3.4.0 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt -xblock-google-drive==0.5.0 +xblock-google-drive==0.6.1 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt @@ -2212,7 +2213,6 @@ xblock-utils==4.0.0 # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt # edx-sga - # xblock-google-drive xmlsec==1.3.13 # via # -r requirements/edx/doc.txt diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index 677a299aee..60684bc3fd 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -52,7 +52,7 @@ async-timeout==4.0.3 # -r requirements/edx/base.txt # aiohttp # redis -attrs==23.1.0 +attrs==23.2.0 # via # -r requirements/edx/base.txt # aiohttp @@ -81,7 +81,7 @@ backports-zoneinfo[tzdata]==0.2.1 # celery # icalendar # kombu -beautifulsoup4==4.12.2 +beautifulsoup4==4.12.3 # via # -r requirements/edx/base.txt # pydata-sphinx-theme @@ -102,13 +102,13 @@ bleach[css]==6.1.0 # xblock-poll boto==2.49.0 # via -r requirements/edx/base.txt -boto3==1.33.12 +boto3==1.34.28 # via # -r requirements/edx/base.txt # django-ses # fs-s3fs # ora2 -botocore==1.33.12 +botocore==1.34.28 # via # -r requirements/edx/base.txt # boto3 @@ -210,7 +210,7 @@ cssutils==2.9.0 # via # -r requirements/edx/base.txt # pynliner -deepmerge==1.1.0 +deepmerge==1.1.1 # via sphinxcontrib-openapi defusedxml==0.7.1 # via @@ -293,6 +293,7 @@ django==3.2.23 # openedx-learning # ora2 # super-csv + # xblock-google-drive # xss-utils django-appconf==1.0.6 # via @@ -343,12 +344,12 @@ django-filter==23.5 # edx-enterprise # lti-consumer-xblock # openedx-blockstore -django-ipware==6.0.2 +django-ipware==6.0.3 # via # -r requirements/edx/base.txt # edx-enterprise # edx-proctoring -django-js-asset==2.1.0 +django-js-asset==2.2.0 # via # -r requirements/edx/base.txt # django-mptt @@ -391,7 +392,7 @@ django-object-actions==4.2.0 # via # -r requirements/edx/base.txt # edx-enterprise -django-pipeline==2.1.0 +django-pipeline==3.0.0 # via -r requirements/edx/base.txt django-ratelimit==4.1.0 # via -r requirements/edx/base.txt @@ -473,11 +474,11 @@ drf-jwt==1.19.2 # via # -r requirements/edx/base.txt # edx-drf-extensions -drf-nested-routers==0.93.4 +drf-nested-routers==0.93.5 # via # -r requirements/edx/base.txt # openedx-blockstore -drf-spectacular==0.27.0 +drf-spectacular==0.27.1 # via -r requirements/edx/base.txt drf-yasg==1.21.5 # via @@ -496,7 +497,7 @@ edx-auth-backends==4.2.0 # via # -r requirements/edx/base.txt # openedx-blockstore -edx-braze-client==0.1.8 +edx-braze-client==0.2.1 # via # -r requirements/edx/base.txt # edx-enterprise @@ -524,7 +525,7 @@ edx-django-release-util==1.3.0 # openedx-blockstore edx-django-sites-extensions==4.0.2 # via -r requirements/edx/base.txt -edx-django-utils==5.9.0 +edx-django-utils==5.10.1 # via # -r requirements/edx/base.txt # django-config-models @@ -540,7 +541,7 @@ edx-django-utils==5.9.0 # openedx-blockstore # ora2 # super-csv -edx-drf-extensions==10.0.0 +edx-drf-extensions==10.1.0 # via # -r requirements/edx/base.txt # edx-completion @@ -552,7 +553,7 @@ edx-drf-extensions==10.0.0 # edx-when # edxval # openedx-learning -edx-enterprise==4.10.9 +edx-enterprise==4.10.11 # via # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt @@ -601,7 +602,7 @@ edx-rest-api-client==5.6.1 # edx-proctoring edx-search==3.8.2 # via -r requirements/edx/base.txt -edx-sga==0.23.0 +edx-sga==0.23.1 # via -r requirements/edx/base.txt edx-submissions==3.6.0 # via @@ -647,7 +648,7 @@ event-tracking==2.2.0 # edx-completion # edx-proctoring # edx-search -fastavro==1.9.1 +fastavro==1.9.3 # via # -r requirements/edx/base.txt # openedx-events @@ -655,7 +656,7 @@ filelock==3.13.1 # via # -r requirements/edx/base.txt # snowflake-connector-python -frozenlist==1.4.0 +frozenlist==1.4.1 # via # -r requirements/edx/base.txt # aiohttp @@ -678,7 +679,7 @@ geoip2==4.8.0 # via -r requirements/edx/base.txt gitdb==4.0.11 # via gitpython -gitpython==3.1.40 +gitpython==3.1.41 # via -r requirements/edx/doc.in glob2==0.7 # via -r requirements/edx/base.txt @@ -701,7 +702,7 @@ idna==3.6 # yarl imagesize==1.4.1 # via sphinx -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 # via # -r requirements/edx/base.txt # markdown @@ -731,7 +732,7 @@ itypes==1.2.0 # via # -r requirements/edx/base.txt # coreapi -jinja2==3.1.2 +jinja2==3.1.3 # via # -r requirements/edx/base.txt # code-annotations @@ -759,22 +760,22 @@ jsonfield==3.1.0 # edx-submissions # lti-consumer-xblock # ora2 -jsonschema==4.20.0 +jsonschema==4.21.1 # via # -r requirements/edx/base.txt # drf-spectacular # optimizely-sdk # sphinxcontrib-openapi -jsonschema-specifications==2023.11.2 +jsonschema-specifications==2023.12.1 # via # -r requirements/edx/base.txt # jsonschema -jwcrypto==1.5.0 +jwcrypto==1.5.1 # via # -r requirements/edx/base.txt # django-oauth-toolkit # pylti1p3 -kombu==5.3.4 +kombu==5.3.5 # via # -r requirements/edx/base.txt # celery @@ -795,10 +796,11 @@ loremipsum==1.0.5 # via # -r requirements/edx/base.txt # ora2 -lti-consumer-xblock==9.8.1 +lti-consumer-xblock==9.8.3 # via -r requirements/edx/base.txt -lxml==4.9.3 +lxml==4.9.4 # via + # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt # edx-i18n-tools # edxval @@ -817,7 +819,6 @@ mako==1.3.0 # acid-xblock # lti-consumer-xblock # xblock - # xblock-google-drive # xblock-utils markdown==3.3.7 # via @@ -830,7 +831,7 @@ markey==0.8 # via # -r requirements/edx/base.txt # enmerkar-underscore -markupsafe==2.1.3 +markupsafe==2.1.4 # via # -r requirements/edx/base.txt # chem @@ -838,7 +839,7 @@ markupsafe==2.1.3 # mako # openedx-calc # xblock -maxminddb==2.5.1 +maxminddb==2.5.2 # via # -r requirements/edx/base.txt # geoip2 @@ -862,11 +863,11 @@ multidict==6.0.4 # -r requirements/edx/base.txt # aiohttp # yarl -mysqlclient==2.2.0 +mysqlclient==2.2.1 # via # -r requirements/edx/base.txt # openedx-blockstore -newrelic==9.3.0 +newrelic==9.6.0 # via # -r requirements/edx/base.txt # edx-django-utils @@ -897,13 +898,13 @@ openai==0.28.1 # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt # edx-enterprise -openedx-atlas==0.5.0 +openedx-atlas==0.6.0 # via -r requirements/edx/base.txt openedx-blockstore==1.4.0 # via -r requirements/edx/base.txt openedx-calc==3.0.1 # via -r requirements/edx/base.txt -openedx-django-pyfs==3.4.0 +openedx-django-pyfs==3.4.1 # via # -r requirements/edx/base.txt # lti-consumer-xblock @@ -928,8 +929,10 @@ openedx-learning==0.4.4 openedx-mongodbproxy==0.2.0 # via -r requirements/edx/base.txt optimizely-sdk==4.1.1 - # via -r requirements/edx/base.txt -ora2==6.0.29 + # via + # -c requirements/edx/../constraints.txt + # -r requirements/edx/base.txt +ora2==6.0.30 # via -r requirements/edx/base.txt packaging==23.2 # via @@ -969,7 +972,7 @@ picobox==4.0.0 # via sphinxcontrib-openapi piexif==1.1.3 # via -r requirements/edx/base.txt -pillow==10.1.0 +pillow==10.2.0 # via # -r requirements/edx/base.txt # edx-enterprise @@ -987,11 +990,11 @@ polib==1.2.0 # via # -r requirements/edx/base.txt # edx-i18n-tools -prompt-toolkit==3.0.42 +prompt-toolkit==3.0.43 # via # -r requirements/edx/base.txt # click-repl -psutil==5.9.6 +psutil==5.9.8 # via # -r requirements/edx/base.txt # edx-django-utils @@ -1009,7 +1012,7 @@ pycparser==2.21 # via # -r requirements/edx/base.txt # cffi -pycryptodomex==3.19.0 +pycryptodomex==3.20.0 # via # -r requirements/edx/base.txt # edx-proctoring @@ -1096,13 +1099,13 @@ python-dateutil==2.8.2 # olxcleaner # ora2 # xblock -python-ipware==2.0.0 +python-ipware==2.0.1 # via # -r requirements/edx/base.txt # django-ipware -python-memcached==1.59 +python-memcached==1.62 # via -r requirements/edx/base.txt -python-slugify==8.0.1 +python-slugify==8.0.2 # via # -r requirements/edx/base.txt # code-annotations @@ -1149,20 +1152,20 @@ pyyaml==6.0.1 # edx-i18n-tools # sphinxcontrib-openapi # xblock -random2==1.0.1 +random2==1.0.2 # via -r requirements/edx/base.txt -recommender-xblock==2.0.1 +recommender-xblock==2.1.1 # via -r requirements/edx/base.txt redis==5.0.1 # via # -r requirements/edx/base.txt # walrus -referencing==0.32.0 +referencing==0.32.1 # via # -r requirements/edx/base.txt # jsonschema # jsonschema-specifications -regex==2023.10.3 +regex==2023.12.25 # via # -r requirements/edx/base.txt # nltk @@ -1190,11 +1193,12 @@ requests==2.31.0 # snowflake-connector-python # social-auth-core # sphinx + # xblock-google-drive requests-oauthlib==1.3.1 # via # -r requirements/edx/base.txt # social-auth-core -rpds-py==0.13.2 +rpds-py==0.17.1 # via # -r requirements/edx/base.txt # jsonschema @@ -1213,7 +1217,7 @@ rules==3.3 # edx-enterprise # edx-proctoring # openedx-learning -s3transfer==0.8.2 +s3transfer==0.10.0 # via # -r requirements/edx/base.txt # boto3 @@ -1268,7 +1272,6 @@ six==1.16.0 # py2neo # pyjwkest # python-dateutil - # python-memcached # sphinxcontrib-httpdomain slumber==0.7.1 # via @@ -1280,7 +1283,7 @@ smmap==5.0.1 # via gitdb snowballstemmer==2.2.0 # via sphinx -snowflake-connector-python==3.6.0 +snowflake-connector-python==3.7.0 # via # -r requirements/edx/base.txt # edx-enterprise @@ -1398,7 +1401,7 @@ typing-extensions==4.9.0 # pydata-sphinx-theme # pylti1p3 # snowflake-connector-python -tzdata==2023.3 +tzdata==2023.4 # via # -r requirements/edx/base.txt # backports-zoneinfo @@ -1440,7 +1443,7 @@ walrus==0.9.3 # edx-event-bus-redis watchdog==3.0.0 # via -r requirements/edx/base.txt -wcwidth==0.2.12 +wcwidth==0.2.13 # via # -r requirements/edx/base.txt # prompt-toolkit @@ -1466,7 +1469,7 @@ wrapt==1.16.0 # via # -r requirements/edx/base.txt # deprecated -xblock[django]==1.9.0 +xblock[django]==1.10.0 # via # -r requirements/edx/base.txt # acid-xblock @@ -1483,9 +1486,9 @@ xblock[django]==1.9.0 # xblock-google-drive # xblock-poll # xblock-utils -xblock-drag-and-drop-v2==3.3.0 +xblock-drag-and-drop-v2==3.4.0 # via -r requirements/edx/base.txt -xblock-google-drive==0.5.0 +xblock-google-drive==0.6.1 # via -r requirements/edx/base.txt xblock-poll==1.13.0 # via -r requirements/edx/base.txt @@ -1493,7 +1496,6 @@ xblock-utils==4.0.0 # via # -r requirements/edx/base.txt # edx-sga - # xblock-google-drive xmlsec==1.3.13 # via # -r requirements/edx/base.txt diff --git a/requirements/edx/paver.txt b/requirements/edx/paver.txt index 7db9d5ea43..7e511c8182 100644 --- a/requirements/edx/paver.txt +++ b/requirements/edx/paver.txt @@ -20,7 +20,7 @@ libsass==0.10.0 # via # -c requirements/edx/../constraints.txt # -r requirements/edx/paver.in -markupsafe==2.1.3 +markupsafe==2.1.4 # via -r requirements/edx/paver.in mock==5.1.0 # via -r requirements/edx/paver.in @@ -30,7 +30,7 @@ paver==1.3.4 # via -r requirements/edx/paver.in pbr==6.0.0 # via stevedore -psutil==5.9.6 +psutil==5.9.8 # via -r requirements/edx/paver.in pymemcache==4.0.0 # via -r requirements/edx/paver.in @@ -39,7 +39,7 @@ pymongo==3.13.0 # -c requirements/edx/../constraints.txt # -r requirements/edx/paver.in # edx-opaque-keys -python-memcached==1.59 +python-memcached==1.62 # via -r requirements/edx/paver.in requests==2.31.0 # via -r requirements/edx/paver.in @@ -47,7 +47,6 @@ six==1.16.0 # via # libsass # paver - # python-memcached stevedore==5.1.0 # via # -r requirements/edx/paver.in diff --git a/requirements/edx/semgrep.txt b/requirements/edx/semgrep.txt index 1d4715a2ed..11bfb4b51d 100644 --- a/requirements/edx/semgrep.txt +++ b/requirements/edx/semgrep.txt @@ -4,7 +4,7 @@ # # make upgrade # -attrs==23.1.0 +attrs==23.2.0 # via # glom # jsonschema @@ -44,9 +44,9 @@ importlib-resources==6.1.1 # via # jsonschema # jsonschema-specifications -jsonschema==4.20.0 +jsonschema==4.21.1 # via semgrep -jsonschema-specifications==2023.11.2 +jsonschema-specifications==2023.12.1 # via jsonschema markdown-it-py==3.0.0 # via rich @@ -60,7 +60,7 @@ pkgutil-resolve-name==1.3.10 # via jsonschema pygments==2.17.2 # via rich -referencing==0.32.0 +referencing==0.32.1 # via # jsonschema # jsonschema-specifications @@ -68,7 +68,7 @@ requests==2.31.0 # via semgrep rich==13.7.0 # via semgrep -rpds-py==0.13.2 +rpds-py==0.17.1 # via # jsonschema # referencing diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 4358a89d7d..f597a2b442 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -31,10 +31,8 @@ aniso8601==9.0.1 # edx-tincan-py35 annotated-types==0.6.0 # via pydantic -anyio==3.7.1 - # via - # fastapi - # starlette +anyio==4.2.0 + # via starlette appdirs==1.4.4 # via # -r requirements/edx/base.txt @@ -58,7 +56,7 @@ async-timeout==4.0.3 # -r requirements/edx/base.txt # aiohttp # redis -attrs==23.1.0 +attrs==23.2.0 # via # -r requirements/edx/base.txt # aiohttp @@ -85,7 +83,7 @@ backports-zoneinfo[tzdata]==0.2.1 # celery # icalendar # kombu -beautifulsoup4==4.12.2 +beautifulsoup4==4.12.3 # via # -r requirements/edx/base.txt # -r requirements/edx/testing.in @@ -106,13 +104,13 @@ bleach[css]==6.1.0 # xblock-poll boto==2.49.0 # via -r requirements/edx/base.txt -boto3==1.33.12 +boto3==1.34.28 # via # -r requirements/edx/base.txt # django-ses # fs-s3fs # ora2 -botocore==1.33.12 +botocore==1.34.28 # via # -r requirements/edx/base.txt # boto3 @@ -210,7 +208,7 @@ coreschema==0.0.4 # -r requirements/edx/base.txt # coreapi # drf-yasg -coverage[toml]==7.3.2 +coverage[toml]==7.4.0 # via # -r requirements/edx/coverage.txt # pytest-cov @@ -237,7 +235,7 @@ cssutils==2.9.0 # via # -r requirements/edx/base.txt # pynliner -ddt==1.7.0 +ddt==1.7.1 # via -r requirements/edx/testing.in defusedxml==0.7.1 # via @@ -250,7 +248,7 @@ deprecated==1.2.14 # via # -r requirements/edx/base.txt # jwcrypto -diff-cover==8.0.1 +diff-cover==8.0.3 # via -r requirements/edx/coverage.txt dill==0.3.7 # via pylint @@ -326,6 +324,7 @@ django==3.2.23 # openedx-learning # ora2 # super-csv + # xblock-google-drive # xss-utils django-appconf==1.0.6 # via @@ -376,12 +375,12 @@ django-filter==23.5 # edx-enterprise # lti-consumer-xblock # openedx-blockstore -django-ipware==6.0.2 +django-ipware==6.0.3 # via # -r requirements/edx/base.txt # edx-enterprise # edx-proctoring -django-js-asset==2.1.0 +django-js-asset==2.2.0 # via # -r requirements/edx/base.txt # django-mptt @@ -424,7 +423,7 @@ django-object-actions==4.2.0 # via # -r requirements/edx/base.txt # edx-enterprise -django-pipeline==2.1.0 +django-pipeline==3.0.0 # via -r requirements/edx/base.txt django-ratelimit==4.1.0 # via -r requirements/edx/base.txt @@ -501,11 +500,11 @@ drf-jwt==1.19.2 # via # -r requirements/edx/base.txt # edx-drf-extensions -drf-nested-routers==0.93.4 +drf-nested-routers==0.93.5 # via # -r requirements/edx/base.txt # openedx-blockstore -drf-spectacular==0.27.0 +drf-spectacular==0.27.1 # via -r requirements/edx/base.txt drf-yasg==1.21.5 # via @@ -524,7 +523,7 @@ edx-auth-backends==4.2.0 # via # -r requirements/edx/base.txt # openedx-blockstore -edx-braze-client==0.1.8 +edx-braze-client==0.2.1 # via # -r requirements/edx/base.txt # edx-enterprise @@ -552,7 +551,7 @@ edx-django-release-util==1.3.0 # openedx-blockstore edx-django-sites-extensions==4.0.2 # via -r requirements/edx/base.txt -edx-django-utils==5.9.0 +edx-django-utils==5.10.1 # via # -r requirements/edx/base.txt # django-config-models @@ -568,7 +567,7 @@ edx-django-utils==5.9.0 # openedx-blockstore # ora2 # super-csv -edx-drf-extensions==10.0.0 +edx-drf-extensions==10.1.0 # via # -r requirements/edx/base.txt # edx-completion @@ -580,7 +579,7 @@ edx-drf-extensions==10.0.0 # edx-when # edxval # openedx-learning -edx-enterprise==4.10.9 +edx-enterprise==4.10.11 # via # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt @@ -632,7 +631,7 @@ edx-rest-api-client==5.6.1 # edx-proctoring edx-search==3.8.2 # via -r requirements/edx/base.txt -edx-sga==0.23.0 +edx-sga==0.23.1 # via -r requirements/edx/base.txt edx-submissions==3.6.0 # via @@ -686,11 +685,11 @@ execnet==2.0.2 # via pytest-xdist factory-boy==3.3.0 # via -r requirements/edx/testing.in -faker==20.1.0 +faker==22.5.1 # via factory-boy -fastapi==0.105.0 +fastapi==0.109.0 # via pact-python -fastavro==1.9.1 +fastavro==1.9.3 # via # -r requirements/edx/base.txt # openedx-events @@ -700,9 +699,9 @@ filelock==3.13.1 # snowflake-connector-python # tox # virtualenv -freezegun==1.3.1 +freezegun==1.4.0 # via -r requirements/edx/testing.in -frozenlist==1.4.0 +frozenlist==1.4.1 # via # -r requirements/edx/base.txt # aiohttp @@ -725,7 +724,7 @@ geoip2==4.8.0 # via -r requirements/edx/base.txt glob2==0.7 # via -r requirements/edx/base.txt -grimp==3.1 +grimp==3.2 # via import-linter gunicorn==21.2.0 # via -r requirements/edx/base.txt @@ -749,9 +748,9 @@ idna==3.6 # requests # snowflake-connector-python # yarl -import-linter==1.12.1 +import-linter==2.0 # via -r requirements/edx/testing.in -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 # via # -r requirements/edx/base.txt # markdown @@ -779,7 +778,7 @@ isodate==0.6.1 # via # -r requirements/edx/base.txt # python3-saml -isort==5.13.1 +isort==5.13.2 # via # -r requirements/edx/testing.in # pylint @@ -787,7 +786,7 @@ itypes==1.2.0 # via # -r requirements/edx/base.txt # coreapi -jinja2==3.1.2 +jinja2==3.1.3 # via # -r requirements/edx/base.txt # -r requirements/edx/coverage.txt @@ -816,21 +815,21 @@ jsonfield==3.1.0 # edx-submissions # lti-consumer-xblock # ora2 -jsonschema==4.20.0 +jsonschema==4.21.1 # via # -r requirements/edx/base.txt # drf-spectacular # optimizely-sdk -jsonschema-specifications==2023.11.2 +jsonschema-specifications==2023.12.1 # via # -r requirements/edx/base.txt # jsonschema -jwcrypto==1.5.0 +jwcrypto==1.5.1 # via # -r requirements/edx/base.txt # django-oauth-toolkit # pylti1p3 -kombu==5.3.4 +kombu==5.3.5 # via # -r requirements/edx/base.txt # celery @@ -843,7 +842,7 @@ lazy==1.6 # lti-consumer-xblock # ora2 # xblock -lazy-object-proxy==1.9.0 +lazy-object-proxy==1.10.0 # via astroid libsass==0.10.0 # via @@ -853,10 +852,11 @@ loremipsum==1.0.5 # via # -r requirements/edx/base.txt # ora2 -lti-consumer-xblock==9.8.1 +lti-consumer-xblock==9.8.3 # via -r requirements/edx/base.txt -lxml==4.9.3 +lxml==4.9.4 # via + # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt # edx-i18n-tools # edxval @@ -876,7 +876,6 @@ mako==1.3.0 # acid-xblock # lti-consumer-xblock # xblock - # xblock-google-drive # xblock-utils markdown==3.3.7 # via @@ -889,7 +888,7 @@ markey==0.8 # via # -r requirements/edx/base.txt # enmerkar-underscore -markupsafe==2.1.3 +markupsafe==2.1.4 # via # -r requirements/edx/base.txt # -r requirements/edx/coverage.txt @@ -898,7 +897,7 @@ markupsafe==2.1.3 # mako # openedx-calc # xblock -maxminddb==2.5.1 +maxminddb==2.5.2 # via # -r requirements/edx/base.txt # geoip2 @@ -922,11 +921,11 @@ multidict==6.0.4 # -r requirements/edx/base.txt # aiohttp # yarl -mysqlclient==2.2.0 +mysqlclient==2.2.1 # via # -r requirements/edx/base.txt # openedx-blockstore -newrelic==9.3.0 +newrelic==9.6.0 # via # -r requirements/edx/base.txt # edx-django-utils @@ -957,13 +956,13 @@ openai==0.28.1 # -c requirements/edx/../constraints.txt # -r requirements/edx/base.txt # edx-enterprise -openedx-atlas==0.5.0 +openedx-atlas==0.6.0 # via -r requirements/edx/base.txt openedx-blockstore==1.4.0 # via -r requirements/edx/base.txt openedx-calc==3.0.1 # via -r requirements/edx/base.txt -openedx-django-pyfs==3.4.0 +openedx-django-pyfs==3.4.1 # via # -r requirements/edx/base.txt # lti-consumer-xblock @@ -988,8 +987,10 @@ openedx-learning==0.4.4 openedx-mongodbproxy==0.2.0 # via -r requirements/edx/base.txt optimizely-sdk==4.1.1 - # via -r requirements/edx/base.txt -ora2==6.0.29 + # via + # -c requirements/edx/../constraints.txt + # -r requirements/edx/base.txt +ora2==6.0.30 # via -r requirements/edx/base.txt packaging==23.2 # via @@ -1030,7 +1031,7 @@ pgpy==0.6.0 # edx-enterprise piexif==1.1.3 # via -r requirements/edx/base.txt -pillow==10.1.0 +pillow==10.2.0 # via # -r requirements/edx/base.txt # edx-enterprise @@ -1047,7 +1048,7 @@ platformdirs==3.11.0 # snowflake-connector-python # tox # virtualenv -pluggy==1.3.0 +pluggy==1.4.0 # via # -r requirements/edx/coverage.txt # diff-cover @@ -1058,11 +1059,11 @@ polib==1.2.0 # -r requirements/edx/base.txt # -r requirements/edx/testing.in # edx-i18n-tools -prompt-toolkit==3.0.42 +prompt-toolkit==3.0.43 # via # -r requirements/edx/base.txt # click-repl -psutil==5.9.6 +psutil==5.9.8 # via # -r requirements/edx/base.txt # edx-django-utils @@ -1088,15 +1089,15 @@ pycparser==2.21 # via # -r requirements/edx/base.txt # cffi -pycryptodomex==3.19.0 +pycryptodomex==3.20.0 # via # -r requirements/edx/base.txt # edx-proctoring # lti-consumer-xblock # pyjwkest -pydantic==2.5.2 +pydantic==2.5.3 # via fastapi -pydantic-core==2.14.5 +pydantic-core==2.14.6 # via pydantic pygments==2.17.2 # via @@ -1184,7 +1185,7 @@ pysrt==1.1.2 # via # -r requirements/edx/base.txt # edxval -pytest==7.4.3 +pytest==7.4.4 # via # -r requirements/edx/testing.in # pylint-pytest @@ -1226,13 +1227,13 @@ python-dateutil==2.8.2 # olxcleaner # ora2 # xblock -python-ipware==2.0.0 +python-ipware==2.0.1 # via # -r requirements/edx/base.txt # django-ipware -python-memcached==1.59 +python-memcached==1.62 # via -r requirements/edx/base.txt -python-slugify==8.0.1 +python-slugify==8.0.2 # via # -r requirements/edx/base.txt # code-annotations @@ -1278,20 +1279,20 @@ pyyaml==6.0.1 # edx-django-release-util # edx-i18n-tools # xblock -random2==1.0.1 +random2==1.0.2 # via -r requirements/edx/base.txt -recommender-xblock==2.0.1 +recommender-xblock==2.1.1 # via -r requirements/edx/base.txt redis==5.0.1 # via # -r requirements/edx/base.txt # walrus -referencing==0.32.0 +referencing==0.32.1 # via # -r requirements/edx/base.txt # jsonschema # jsonschema-specifications -regex==2023.10.3 +regex==2023.12.25 # via # -r requirements/edx/base.txt # nltk @@ -1319,11 +1320,12 @@ requests==2.31.0 # slumber # snowflake-connector-python # social-auth-core + # xblock-google-drive requests-oauthlib==1.3.1 # via # -r requirements/edx/base.txt # social-auth-core -rpds-py==0.13.2 +rpds-py==0.17.1 # via # -r requirements/edx/base.txt # jsonschema @@ -1342,7 +1344,7 @@ rules==3.3 # edx-enterprise # edx-proctoring # openedx-learning -s3transfer==0.8.2 +s3transfer==0.10.0 # via # -r requirements/edx/base.txt # boto3 @@ -1401,7 +1403,6 @@ six==1.16.0 # py2neo # pyjwkest # python-dateutil - # python-memcached slumber==0.7.1 # via # -r requirements/edx/base.txt @@ -1410,7 +1411,7 @@ slumber==0.7.1 # edx-rest-api-client sniffio==1.3.0 # via anyio -snowflake-connector-python==3.6.0 +snowflake-connector-python==3.7.0 # via # -r requirements/edx/base.txt # edx-enterprise @@ -1444,7 +1445,7 @@ sqlparse==0.4.4 # openedx-blockstore staff-graded-xblock==2.2.0 # via -r requirements/edx/base.txt -starlette==0.27.0 +starlette==0.35.1 # via fastapi stevedore==5.1.0 # via @@ -1499,6 +1500,7 @@ typing-extensions==4.9.0 # via # -r requirements/edx/base.txt # annotated-types + # anyio # asgiref # astroid # django-countries @@ -1516,7 +1518,7 @@ typing-extensions==4.9.0 # snowflake-connector-python # starlette # uvicorn -tzdata==2023.3 +tzdata==2023.4 # via # -r requirements/edx/base.txt # backports-zoneinfo @@ -1544,7 +1546,7 @@ urllib3==1.26.18 # snowflake-connector-python user-util==1.0.0 # via -r requirements/edx/base.txt -uvicorn==0.24.0.post1 +uvicorn==0.27.0 # via pact-python vine==5.1.0 # via @@ -1564,7 +1566,7 @@ walrus==0.9.3 # edx-event-bus-redis watchdog==3.0.0 # via -r requirements/edx/base.txt -wcwidth==0.2.12 +wcwidth==0.2.13 # via # -r requirements/edx/base.txt # prompt-toolkit @@ -1591,7 +1593,7 @@ wrapt==1.16.0 # -r requirements/edx/base.txt # astroid # deprecated -xblock[django]==1.9.0 +xblock[django]==1.10.0 # via # -r requirements/edx/base.txt # acid-xblock @@ -1608,9 +1610,9 @@ xblock[django]==1.9.0 # xblock-google-drive # xblock-poll # xblock-utils -xblock-drag-and-drop-v2==3.3.0 +xblock-drag-and-drop-v2==3.4.0 # via -r requirements/edx/base.txt -xblock-google-drive==0.5.0 +xblock-google-drive==0.6.1 # via -r requirements/edx/base.txt xblock-poll==1.13.0 # via -r requirements/edx/base.txt @@ -1618,7 +1620,6 @@ xblock-utils==4.0.0 # via # -r requirements/edx/base.txt # edx-sga - # xblock-google-drive xmlsec==1.3.13 # via # -r requirements/edx/base.txt diff --git a/requirements/pip-tools.txt b/requirements/pip-tools.txt index d0f943174e..e094d58f49 100644 --- a/requirements/pip-tools.txt +++ b/requirements/pip-tools.txt @@ -10,7 +10,7 @@ click==8.1.6 # via # -c requirements/constraints.txt # pip-tools -importlib-metadata==7.0.0 +importlib-metadata==7.0.1 # via build packaging==23.2 # via build diff --git a/requirements/pip.txt b/requirements/pip.txt index 14cb99cd39..a4cf5307d6 100644 --- a/requirements/pip.txt +++ b/requirements/pip.txt @@ -8,7 +8,7 @@ wheel==0.42.0 # via -r requirements/pip.in # The following packages are considered to be unsafe in a requirements file: -pip==23.3.1 +pip==23.3.2 # via -r requirements/pip.in -setuptools==69.0.2 +setuptools==69.0.3 # via -r requirements/pip.in