diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25f1ab5c..a39157dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: django-version: ["pinned"] steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} architecture: x64 @@ -41,7 +41,7 @@ jobs: python-version: ["3.12"] steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} architecture: x64 diff --git a/.github/workflows/mysql8-migrations-check.yml b/.github/workflows/mysql8-migrations-check.yml index 27acc283..4fca5fd5 100644 --- a/.github/workflows/mysql8-migrations-check.yml +++ b/.github/workflows/mysql8-migrations-check.yml @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} diff --git a/api-compact.yaml b/api-compact.yaml index df0eaa6c..fe125309 100644 --- a/api-compact.yaml +++ b/api-compact.yaml @@ -24,6 +24,8 @@ apigateway_responses: &apigateway_responses statusCode: "403" 404: statusCode: "404" + 423: + statusCode: "423" 429: statusCode: "429" 500: @@ -112,6 +114,8 @@ responses: &responses description: "Forbidden" 404: description: "Not Found" + 423: + description: "Locked" 429: description: "Too Many Requests" 500: diff --git a/api.yaml b/api.yaml index 10cc98a0..f9f65b59 100644 --- a/api.yaml +++ b/api.yaml @@ -13,6 +13,8 @@ apigateway_responses: statusCode: "403" "404": statusCode: "404" + "423": + statusCode: "423" "429": statusCode: "429" "500": @@ -92,6 +94,8 @@ responses: description: Forbidden "404": description: Not Found + "423": + description: Locked "429": description: Too Many Requests "500": @@ -224,6 +228,8 @@ endpoints: description: Forbidden "404": description: Not Found + "423": + description: Locked "429": description: Too Many Requests "500": @@ -240,6 +246,8 @@ endpoints: statusCode: "403" "404": statusCode: "404" + "423": + statusCode: "423" "429": statusCode: "429" "500": @@ -380,6 +388,8 @@ endpoints: description: Forbidden "404": description: Not Found + "423": + description: Locked "429": description: Too Many Requests "500": @@ -396,6 +406,8 @@ endpoints: statusCode: "403" "404": statusCode: "404" + "423": + statusCode: "423" "429": statusCode: "429" "500": diff --git a/license_manager/apps/api/serializers.py b/license_manager/apps/api/serializers.py index 2215e073..c6795d21 100644 --- a/license_manager/apps/api/serializers.py +++ b/license_manager/apps/api/serializers.py @@ -354,6 +354,12 @@ class Meta: 'net_days_until_expiration', 'subscription_for_auto_applied_licenses', 'available_subscription_catalogs', + 'enable_auto_applied_subscriptions_with_universal_link', + 'has_custom_license_expiration_messaging', + 'modal_header_text', + 'expired_subscription_modal_messaging', + 'button_label_in_modal', + 'url_for_button_in_modal', ] def get_subscription_for_auto_applied_licenses(self, obj): diff --git a/license_manager/apps/api/v1/tests/test_api_eventing.py b/license_manager/apps/api/v1/tests/test_api_eventing.py index 7f962c0d..e1038e09 100644 --- a/license_manager/apps/api/v1/tests/test_api_eventing.py +++ b/license_manager/apps/api/v1/tests/test_api_eventing.py @@ -246,7 +246,7 @@ def test_activate_an_assigned_license(self, _): url = reverse('api:v1:license-activation') + '/?' + query_params.urlencode() response = self.api_client.post(url) - assert status.HTTP_204_NO_CONTENT == response.status_code + assert status.HTTP_200_OK == response.status_code license_to_be_activated.refresh_from_db() assert mock_activated_track_event.call_count == 1 diff --git a/license_manager/apps/api/v1/tests/test_license_activation_view.py b/license_manager/apps/api/v1/tests/test_license_activation_view.py index aeab03a3..7adf1f76 100644 --- a/license_manager/apps/api/v1/tests/test_license_activation_view.py +++ b/license_manager/apps/api/v1/tests/test_license_activation_view.py @@ -145,8 +145,18 @@ def test_activate_an_assigned_license(self, mock_send_post_activation_email_task with freeze_time(self.now): response = self._post_request(str(self.activation_key)) - assert status.HTTP_204_NO_CONTENT == response.status_code + # Verify that the response contains activated subscription license. + assert status.HTTP_200_OK == response.status_code + activated_license = response.json() + assert activated_license['uuid'] == str(license_to_be_activated.uuid) + assert activated_license['status'] == constants.ACTIVATED + expected_activation_date = self.now.strftime('%Y-%m-%dT%H:%M:%S.%fZ') + assert activated_license['activation_date'] == expected_activation_date + + # Refresh license from the database license_to_be_activated.refresh_from_db() + + # Verify that the license has been activated in the DB assert constants.ACTIVATED == license_to_be_activated.status assert self.lms_user_id == license_to_be_activated.lms_user_id assert self.now == license_to_be_activated.activation_date @@ -159,7 +169,7 @@ def test_activate_an_assigned_license(self, mock_send_post_activation_email_task self.user.email, ) - def test_license_already_activated_returns_204(self): + def test_license_already_activated_returns_200(self): self._assign_learner_roles( jwt_payload_extra={ 'user_id': self.lms_user_id, @@ -174,7 +184,13 @@ def test_license_already_activated_returns_204(self): response = self._post_request(str(self.activation_key)) - assert status.HTTP_204_NO_CONTENT == response.status_code + assert status.HTTP_200_OK == response.status_code + activated_license = response.json() + assert activated_license['uuid'] == str(already_activated_license.uuid) + assert activated_license['status'] == constants.ACTIVATED + expected_activation_date = self.now.strftime('%Y-%m-%dT%H:%M:%S.%fZ') + assert activated_license['activation_date'] == expected_activation_date + already_activated_license.refresh_from_db() assert constants.ACTIVATED == already_activated_license.status assert self.lms_user_id == already_activated_license.lms_user_id @@ -217,7 +233,12 @@ def test_duplicate_licenses_are_cleaned_up(self, mock_license_clean, mock_email_ with freeze_time(self.now): response = self._post_request(str(self.activation_key)) - assert status.HTTP_204_NO_CONTENT == response.status_code + assert status.HTTP_200_OK == response.status_code + activated_license = response.json() + assert activated_license['uuid'] == str(license_b.uuid) + assert activated_license['status'] == constants.ACTIVATED + expected_activation_date = self.now.strftime('%Y-%m-%dT%H:%M:%S.%fZ') + assert activated_license['activation_date'] == expected_activation_date license_b.refresh_from_db() assert constants.ACTIVATED == license_b.status @@ -268,7 +289,12 @@ def test_activated_license_exists_with_duplicates(self, mock_license_clean, mock with freeze_time(self.now): response = self._post_request(str(license_a_activation_key)) - assert status.HTTP_204_NO_CONTENT == response.status_code + assert status.HTTP_200_OK == response.status_code + activated_license = response.json() + assert activated_license['uuid'] == str(license_b.uuid) + assert activated_license['status'] == constants.ACTIVATED + expected_activation_date = self.now.strftime('%Y-%m-%dT%H:%M:%S.%fZ') + assert activated_license['activation_date'] == expected_activation_date license_b.refresh_from_db() assert constants.ACTIVATED == license_b.status @@ -341,7 +367,13 @@ def test_activating_renewed_assigned_license(self, mock_send_post_activation_ema with freeze_time(self.now): response = self._post_request(str(self.activation_key)) - assert status.HTTP_204_NO_CONTENT == response.status_code + assert status.HTTP_200_OK == response.status_code + activated_license = response.json() + assert activated_license['uuid'] == str(current_assigned_license.uuid) + assert activated_license['status'] == constants.ACTIVATED + expected_activation_date = self.now.strftime('%Y-%m-%dT%H:%M:%S.%fZ') + assert activated_license['activation_date'] == expected_activation_date + current_assigned_license.refresh_from_db() prior_assigned_license.refresh_from_db() assert prior_assigned_license.activation_date != self.now diff --git a/license_manager/apps/api/v1/views.py b/license_manager/apps/api/v1/views.py index 24c99388..5757b862 100644 --- a/license_manager/apps/api/v1/views.py +++ b/license_manager/apps/api/v1/views.py @@ -1779,10 +1779,10 @@ def post(self, request): license's subscription plan. * 404 Not Found - if the email found in the request's JWT and the provided ``activation_key`` do not match those of any existing license in an activate subscription plan. - * 204 No Content - if such a license was found, and if the license is currently ``assigned``, + * 200 OK - if such a license was found, and if the license is currently ``assigned``, it is updated with a status of ``activated``, its ``activation_date`` is set, and its ``lms_user_id`` is updated to the value found in the request's JWT. If the license is already ``activated``, - no update is made to it. + no update is made to it. The activated license is then returned in the response. * 422 Unprocessable Entity - if we find a license, but it's status is not currently ``assigned`` or ``activated``, we do nothing and return a 422 with a message indicating that the license cannot be activated. @@ -1801,7 +1801,8 @@ def post(self, request): # There's an implied logical branch where the license is already activated # in which case we also return as if the activation action was successful. - return Response(status=status.HTTP_204_NO_CONTENT) + serialized_license = serializers.LicenseSerializer(user_license) + return Response(serialized_license.data, status=status.HTTP_200_OK) def _track_and_notify(self, user_license): """ diff --git a/license_manager/apps/subscriptions/admin.py b/license_manager/apps/subscriptions/admin.py index 0879002c..17d64662 100644 --- a/license_manager/apps/subscriptions/admin.py +++ b/license_manager/apps/subscriptions/admin.py @@ -416,7 +416,13 @@ class CustomerAgreementAdmin(admin.ModelAdmin): 'default_enterprise_catalog_uuid', 'disable_expiration_notifications', 'license_duration_before_purge', - 'disable_onboarding_notifications' + 'disable_onboarding_notifications', + 'enable_auto_applied_subscriptions_with_universal_link', + 'has_custom_license_expiration_messaging', + 'modal_header_text', + 'expired_subscription_modal_messaging', + 'button_label_in_modal', + 'url_for_button_in_modal', ) custom_fields = ('subscription_for_auto_applied_licenses',) diff --git a/license_manager/apps/subscriptions/constants.py b/license_manager/apps/subscriptions/constants.py index 492e3501..8782e9c7 100644 --- a/license_manager/apps/subscriptions/constants.py +++ b/license_manager/apps/subscriptions/constants.py @@ -25,6 +25,14 @@ class LicenseTypesToRenew: ) +class SubscriptionPlanShouldAutoApplyLicensesChoices: + CHOICES = ( + (None, "----------"), + (True, "Yes"), + (False, "No") + ) + + class SubscriptionPlanChangeReasonChoices: NONE = None NEW = "new" diff --git a/license_manager/apps/subscriptions/forms.py b/license_manager/apps/subscriptions/forms.py index e6324795..4e96753e 100644 --- a/license_manager/apps/subscriptions/forms.py +++ b/license_manager/apps/subscriptions/forms.py @@ -19,6 +19,7 @@ MAX_NUM_LICENSES, MIN_NUM_LICENSES, SubscriptionPlanChangeReasonChoices, + SubscriptionPlanShouldAutoApplyLicensesChoices, ) from license_manager.apps.subscriptions.models import ( CustomerAgreement, @@ -40,6 +41,14 @@ class SubscriptionPlanForm(forms.ModelForm): """ Form used for the SubscriptionPlan admin class. """ + + should_auto_apply_licenses = forms.ChoiceField( + choices=SubscriptionPlanShouldAutoApplyLicensesChoices.CHOICES, + required=False, + label="Should auto apply licenses", + help_text="Whether licenses from this Subscription Plan should be auto applied." + ) + # Extra form field to specify the number of licenses to be associated with the subscription plan num_licenses = forms.IntegerField( label="Number of Licenses", @@ -276,15 +285,15 @@ def populate_subscription_for_auto_applied_licenses_choices(self, instance): start_date__lte=now, expiration_date__gte=now ) - current_plan = instance.auto_applicable_subscription - empty_choice = ('', '------') choices = [empty_choice] + [(plan.uuid, plan.title) for plan in active_plans] choice_field = forms.ChoiceField( choices=choices, required=False, - initial=empty_choice if not current_plan else (current_plan.uuid, current_plan.title) + initial=empty_choice if not current_plan else (current_plan.uuid, current_plan.title), + help_text="The subscription plan to be associated with auto apply licenses. Selecting a license" + " will automatically enable the \"Should auto apply licenses\" field on the subscription plan" ) self.fields['subscription_for_auto_applied_licenses'] = choice_field diff --git a/license_manager/apps/subscriptions/migrations/0070_customeragreement_expired_subscription_modal_messaging_and_more.py b/license_manager/apps/subscriptions/migrations/0070_customeragreement_expired_subscription_modal_messaging_and_more.py new file mode 100644 index 00000000..d0691e7e --- /dev/null +++ b/license_manager/apps/subscriptions/migrations/0070_customeragreement_expired_subscription_modal_messaging_and_more.py @@ -0,0 +1,53 @@ +# Generated by Django 4.2.16 on 2024-09-27 13:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('subscriptions', '0069_alter_customeragreement_disable_expiration_notifications_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='customeragreement', + name='expired_subscription_modal_messaging', + field=models.CharField(blank=True, help_text='The content of a modal that will appear to learners upon subscription expiration. This text can be used for custom guidance per customer.', max_length=512, null=True), + ), + migrations.AddField( + model_name='customeragreement', + name='has_custom_license_expiration_messaging', + field=models.BooleanField(default=False, help_text='Indicates if the customer has a unique license expiration experience, instead of the standard one.'), + ), + migrations.AddField( + model_name='customeragreement', + name='hyper_link_text_for_expired_modal', + field=models.CharField(blank=True, help_text='The display text for the link that will be embedded at the end of the custom expiration modal.', max_length=255, null=True), + ), + migrations.AddField( + model_name='customeragreement', + name='url_for_expired_modal', + field=models.CharField(blank=True, help_text='The underlying url that will be embedded as a hyperlink at the end of the custom expiration modal.', max_length=512, null=True), + ), + migrations.AddField( + model_name='historicalcustomeragreement', + name='expired_subscription_modal_messaging', + field=models.CharField(blank=True, help_text='The content of a modal that will appear to learners upon subscription expiration. This text can be used for custom guidance per customer.', max_length=512, null=True), + ), + migrations.AddField( + model_name='historicalcustomeragreement', + name='has_custom_license_expiration_messaging', + field=models.BooleanField(default=False, help_text='Indicates if the customer has a unique license expiration experience, instead of the standard one.'), + ), + migrations.AddField( + model_name='historicalcustomeragreement', + name='hyper_link_text_for_expired_modal', + field=models.CharField(blank=True, help_text='The display text for the link that will be embedded at the end of the custom expiration modal.', max_length=255, null=True), + ), + migrations.AddField( + model_name='historicalcustomeragreement', + name='url_for_expired_modal', + field=models.CharField(blank=True, help_text='The underlying url that will be embedded as a hyperlink at the end of the custom expiration modal.', max_length=512, null=True), + ), + ] diff --git a/license_manager/apps/subscriptions/migrations/0071_customeragreement_enable_auto_applied_subscriptions_with_universal_link_and_more.py b/license_manager/apps/subscriptions/migrations/0071_customeragreement_enable_auto_applied_subscriptions_with_universal_link_and_more.py new file mode 100644 index 00000000..67d12885 --- /dev/null +++ b/license_manager/apps/subscriptions/migrations/0071_customeragreement_enable_auto_applied_subscriptions_with_universal_link_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.16 on 2024-10-07 15:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('subscriptions', '0070_customeragreement_expired_subscription_modal_messaging_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='customeragreement', + name='enable_auto_applied_subscriptions_with_universal_link', + field=models.BooleanField(default=False, help_text='By default, auto-applied subscriptions are only granted when learners join their enterprise via SSO, checking this box will enable subscription licenses to be applied when a learner joins the enterprise via Universal link as well'), + ), + migrations.AddField( + model_name='historicalcustomeragreement', + name='enable_auto_applied_subscriptions_with_universal_link', + field=models.BooleanField(default=False, help_text='By default, auto-applied subscriptions are only granted when learners join their enterprise via SSO, checking this box will enable subscription licenses to be applied when a learner joins the enterprise via Universal link as well'), + ), + ] diff --git a/license_manager/apps/subscriptions/migrations/0072_customeragreement_button_label_in_modal_and_more.py b/license_manager/apps/subscriptions/migrations/0072_customeragreement_button_label_in_modal_and_more.py new file mode 100644 index 00000000..623c0f4f --- /dev/null +++ b/license_manager/apps/subscriptions/migrations/0072_customeragreement_button_label_in_modal_and_more.py @@ -0,0 +1,53 @@ +# Generated by Django 4.2.16 on 2024-10-18 09:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('subscriptions', '0071_customeragreement_enable_auto_applied_subscriptions_with_universal_link_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='customeragreement', + name='button_label_in_modal', + field=models.CharField(blank=True, help_text='The text that will appear as on the button in the expiration modal', max_length=255, null=True), + ), + migrations.AddField( + model_name='customeragreement', + name='modal_header_text', + field=models.CharField(blank=True, help_text='The bold text that will appear as the header in the expiration modal.', max_length=512, null=True), + ), + migrations.AddField( + model_name='customeragreement', + name='url_for_button_in_modal', + field=models.CharField(blank=True, help_text='The URL that should underly the sole button in the expiration modal', max_length=512, null=True), + ), + migrations.AddField( + model_name='historicalcustomeragreement', + name='button_label_in_modal', + field=models.CharField(blank=True, help_text='The text that will appear as on the button in the expiration modal', max_length=255, null=True), + ), + migrations.AddField( + model_name='historicalcustomeragreement', + name='modal_header_text', + field=models.CharField(blank=True, help_text='The bold text that will appear as the header in the expiration modal.', max_length=512, null=True), + ), + migrations.AddField( + model_name='historicalcustomeragreement', + name='url_for_button_in_modal', + field=models.CharField(blank=True, help_text='The URL that should underly the sole button in the expiration modal', max_length=512, null=True), + ), + migrations.AlterField( + model_name='customeragreement', + name='expired_subscription_modal_messaging', + field=models.TextField(blank=True, help_text='The content of a modal that will appear to learners upon subscription expiration. This text can be used for custom guidance per customer.', null=True), + ), + migrations.AlterField( + model_name='historicalcustomeragreement', + name='expired_subscription_modal_messaging', + field=models.TextField(blank=True, help_text='The content of a modal that will appear to learners upon subscription expiration. This text can be used for custom guidance per customer.', null=True), + ), + ] diff --git a/license_manager/apps/subscriptions/migrations/0073_remove_customeragreement_hyper_link_text_for_expired_modal_and_more.py b/license_manager/apps/subscriptions/migrations/0073_remove_customeragreement_hyper_link_text_for_expired_modal_and_more.py new file mode 100644 index 00000000..14a31ba8 --- /dev/null +++ b/license_manager/apps/subscriptions/migrations/0073_remove_customeragreement_hyper_link_text_for_expired_modal_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.16 on 2024-10-23 08:16 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('subscriptions', '0072_customeragreement_button_label_in_modal_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='customeragreement', + name='hyper_link_text_for_expired_modal', + ), + migrations.RemoveField( + model_name='customeragreement', + name='url_for_expired_modal', + ), + migrations.RemoveField( + model_name='historicalcustomeragreement', + name='hyper_link_text_for_expired_modal', + ), + migrations.RemoveField( + model_name='historicalcustomeragreement', + name='url_for_expired_modal', + ), + ] diff --git a/license_manager/apps/subscriptions/models.py b/license_manager/apps/subscriptions/models.py index 933e9aba..b26071f7 100644 --- a/license_manager/apps/subscriptions/models.py +++ b/license_manager/apps/subscriptions/models.py @@ -48,6 +48,7 @@ track_event, track_license_changes, ) +from license_manager.apps.subscriptions.sanitize import sanitize_html from license_manager.apps.subscriptions.utils import ( days_until, get_license_activation_link, @@ -143,6 +144,58 @@ class CustomerAgreement(TimeStampedModel): ), ) + has_custom_license_expiration_messaging = models.BooleanField( + default=False, + help_text=_( + "Indicates if the customer has a unique license expiration experience, instead of the standard one." + ) + ) + + modal_header_text = models.CharField( + max_length=512, + blank=True, + null=True, + help_text=_( + "The bold text that will appear as the header in the expiration modal." + ) + ) + + expired_subscription_modal_messaging = models.TextField( + blank=True, + null=True, + help_text=_( + "The content of a modal that will appear to learners upon subscription expiration. This text can be used " + "for custom guidance per customer." + ) + ) + + button_label_in_modal = models.CharField( + max_length=255, + blank=True, + null=True, + help_text=_( + "The text that will appear as on the button in the expiration modal" + ) + ) + + url_for_button_in_modal = models.CharField( + max_length=512, + blank=True, + null=True, + help_text=_( + "The URL that should underly the sole button in the expiration modal" + ) + ) + + enable_auto_applied_subscriptions_with_universal_link = models.BooleanField( + default=False, + help_text=_( + "By default, auto-applied subscriptions are only granted when learners join their enterprise via SSO, " + "checking this box will enable subscription licenses to be applied when a learner joins the enterprise via " + "Universal link as well" + ) + ) + history = HistoricalRecords() @property @@ -192,6 +245,53 @@ class Meta: verbose_name = _("Customer Agreement") verbose_name_plural = _("Customer Agreements") + def clean(self): + """ + Custom clean method to validate fields based on the 'Has Custom License Expiration Messaging' flag. + """ + errors = {} + + # Sanitize the expired_subscription_modal_messaging field + if self.expired_subscription_modal_messaging: + self.expired_subscription_modal_messaging = sanitize_html(self.expired_subscription_modal_messaging) + + error_message = "This field cannot be blank if 'Has Custom License Expiration Messaging' is checked." + # Validate fields when custom messaging is enabled + if self.has_custom_license_expiration_messaging: + required_fields = { + "modal_header_text": error_message, + "expired_subscription_modal_messaging": error_message, + "button_label_in_modal": error_message, + "url_for_button_in_modal": error_message, + } + + # Check if any required fields are missing + for field, error_message in required_fields.items(): + if not getattr(self, field): + errors[field] = error_message + + # Ensure all fields are blank if custom messaging is disabled + if not self.has_custom_license_expiration_messaging: + fields_to_check = [ + "modal_header_text", + "expired_subscription_modal_messaging", + "button_label_in_modal", + "url_for_button_in_modal", + ] + if any(getattr(self, field) for field in fields_to_check): + error_msg = "This field must be blank if 'Has Custom License Expiration Messaging' is unchecked." + errors = {field: error_msg for field in fields_to_check} + + # Validate that url_for_button_in_modal is a complete URL + if self.url_for_button_in_modal and not self.url_for_button_in_modal.startswith(("http://", "https://")): + errors["url_for_button_in_modal"] = ( + "The URL must start with 'http://' or 'https://'. Please provide a valid URL." + ) + + # Raise ValidationError if there are any errors + if errors: + raise ValidationError(errors) + def __str__(self): """ Return human-readable string representation. @@ -521,6 +621,7 @@ def num_revocations_remaining(self): num_revocations_allowed = ceil(self.num_licenses * (self.revoke_max_percentage / 100)) return num_revocations_allowed - self.num_revocations_applied + num_revocations_remaining.fget.short_description = "Number of Revocations Remaining" @property @@ -961,6 +1062,7 @@ class License(TimeStampedModel): .. pii_types: id,email_address .. pii_retirement: local_api """ + class Meta: indexes = [ models.Index(fields=["subscription_plan", "status"], name="subscription_plan_status_idx"), diff --git a/license_manager/apps/subscriptions/sanitize.py b/license_manager/apps/subscriptions/sanitize.py new file mode 100644 index 00000000..27a050ba --- /dev/null +++ b/license_manager/apps/subscriptions/sanitize.py @@ -0,0 +1,33 @@ +import bleach +from bleach.css_sanitizer import CSSSanitizer + + +def sanitize_html(html_content): + """ + Sanitize HTML content to allow only safe tags and attributes, + while disallowing JavaScript and unsafe protocols. + """ + # Define allowed tags and attributes + allowed_tags = set.union(set(bleach.ALLOWED_TAGS), {"span"}) # Allow all standard HTML tags + allowed_attrs = {"*": ["className", "class", "style", "id"]} + css_sanitizer = CSSSanitizer(allowed_css_properties=["color", "font-weight"]) + + # Clean the HTML content + sanitized_content = bleach.clean( + html_content, + tags=allowed_tags, + attributes=allowed_attrs, + strip=True, # Strip disallowed tags completely + protocols=["http", "https"], # Only allow http and https URLs, + css_sanitizer=css_sanitizer, + ) + + # Use bleach.linkify to ensure no javascript: links in tags + sanitized_content = bleach.linkify( + sanitized_content, + callbacks=[ + bleach.callbacks.nofollow + ], # Apply 'nofollow' to external links for safety + ) + + return sanitized_content diff --git a/license_manager/apps/subscriptions/tests/factories.py b/license_manager/apps/subscriptions/tests/factories.py index 96db577c..02aa39a7 100644 --- a/license_manager/apps/subscriptions/tests/factories.py +++ b/license_manager/apps/subscriptions/tests/factories.py @@ -54,6 +54,7 @@ class Meta: default_enterprise_catalog_uuid = factory.LazyFunction(uuid4) enterprise_customer_name = factory.LazyAttribute(lambda x: FAKER.company()) disable_onboarding_notifications = False + enable_auto_applied_subscriptions_with_universal_link = False class PlanTypeFactory(factory.django.DjangoModelFactory): diff --git a/license_manager/apps/subscriptions/tests/test_admin.py b/license_manager/apps/subscriptions/tests/test_admin.py index e2bab224..019db949 100644 --- a/license_manager/apps/subscriptions/tests/test_admin.py +++ b/license_manager/apps/subscriptions/tests/test_admin.py @@ -114,7 +114,10 @@ def test_select_subscription_for_auto_applied_licenses(mock_toggle_auto_apply_li request = RequestFactory() request.user = UserFactory() customer_agreement = CustomerAgreementFactory() - subscription_plan = SubscriptionPlanFactory(customer_agreement=customer_agreement) + subscription_plan = SubscriptionPlanFactory( + customer_agreement=customer_agreement, + ) + customer_agreement_uuid = str(customer_agreement.uuid) subscription_uuid = str(subscription_plan.uuid) request.resolver_match = mock.Mock(kwargs={'object_id': customer_agreement_uuid}) diff --git a/license_manager/apps/subscriptions/tests/test_forms.py b/license_manager/apps/subscriptions/tests/test_forms.py index c7154ba8..6563e871 100644 --- a/license_manager/apps/subscriptions/tests/test_forms.py +++ b/license_manager/apps/subscriptions/tests/test_forms.py @@ -241,10 +241,11 @@ def test_populate_subscription_for_auto_applied_licenses_choices(self): field = form.fields['subscription_for_auto_applied_licenses'] choices = field.choices + initial = field.initial self.assertEqual(len(choices), 2) self.assertEqual(choices[0], ('', '------')) self.assertEqual(choices[1], (active_subscription_plan.uuid, active_subscription_plan.title)) - self.assertEqual(field.initial, ('', '------')) + self.assertEqual(initial, ('', '------')) def test_populate_subscription_for_auto_applied_licenses_choices_initial_choice(self): customer_agreement = CustomerAgreementFactory() diff --git a/pylintrc b/pylintrc index d857e0e4..6edf14d3 100644 --- a/pylintrc +++ b/pylintrc @@ -64,7 +64,7 @@ # SERIOUSLY. # # ------------------------------ -# Generated by edx-lint version: 5.3.6 +# Generated by edx-lint version: 5.4.0 # ------------------------------ [MASTER] ignore = ,migrations, settings, wsgi.py @@ -304,6 +304,7 @@ disable = no-member, useless-option-value, unknown-option-value, + too-many-positional-arguments, [REPORTS] output-format = text @@ -400,4 +401,4 @@ int-import-graph = [EXCEPTIONS] overgeneral-exceptions = builtins.Exception -# 2fe5a71dc9c880b7985a7eefc288cd0ada05602f +# ea586deca5871e992466c748232382f9dfadff18 diff --git a/pylintrc_tweaks b/pylintrc_tweaks index b3b778e4..514d4015 100644 --- a/pylintrc_tweaks +++ b/pylintrc_tweaks @@ -23,3 +23,4 @@ disable+ = no-member, useless-option-value, unknown-option-value, + too-many-positional-arguments, diff --git a/requirements/base.in b/requirements/base.in index e7bdd14f..bb7c126d 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -40,3 +40,5 @@ rules simplejson zipp django-log-request-id +bleach +bleach[css] diff --git a/requirements/base.txt b/requirements/base.txt index d3e83fd7..1fd509c8 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -20,11 +20,13 @@ backoff==1.10.0 # via # -r requirements/base.in # analytics-python -billiard==4.2.0 +billiard==4.2.1 # via celery -boto3==1.35.19 +bleach[css]==6.1.0 + # via -r requirements/base.in +boto3==1.35.49 # via django-ses -botocore==1.35.19 +botocore==1.35.49 # via # boto3 # s3transfer @@ -39,7 +41,7 @@ cffi==1.17.1 # via # cryptography # pynacl -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via requests click==8.1.7 # via @@ -57,7 +59,7 @@ click-repl==0.3.0 # via celery code-annotations==1.8.0 # via edx-toggles -cryptography==43.0.1 +cryptography==43.0.3 # via # pyjwt # social-auth-core @@ -97,7 +99,7 @@ django-autocomplete-light==3.11.0 # via -r requirements/base.in django-celery-results==2.5.1 # via -r requirements/base.in -django-cors-headers==4.4.0 +django-cors-headers==4.5.0 # via -r requirements/base.in django-crum==0.7.9 # via @@ -117,7 +119,7 @@ django-model-utils==5.0.0 # -r requirements/base.in # edx-celeryutils # edx-rbac -django-ses==4.1.1 +django-ses==4.2.0 # via -r requirements/base.in django-simple-history==3.7.0 # via -r requirements/base.in @@ -139,7 +141,7 @@ djangorestframework==3.15.2 # edx-drf-extensions djangorestframework-csv==3.0.2 # via -r requirements/base.in -dnspython==2.6.1 +dnspython==2.7.0 # via pymongo drf-jwt==1.19.2 # via edx-drf-extensions @@ -153,13 +155,14 @@ edx-braze-client==0.2.5 # via -r requirements/base.in edx-celeryutils==1.3.0 # via -r requirements/base.in -edx-django-utils==5.15.0 +edx-django-utils==6.1.0 # via + # -c requirements/constraints.txt # -r requirements/base.in # edx-drf-extensions # edx-rest-api-client # edx-toggles -edx-drf-extensions==10.4.0 +edx-drf-extensions==10.5.0 # via # -r requirements/base.in # edx-rbac @@ -185,17 +188,17 @@ jsonfield==3.1.0 # via edx-celeryutils jsonschema==4.23.0 # via drf-spectacular -jsonschema-specifications==2023.12.1 +jsonschema-specifications==2024.10.1 # via jsonschema -kombu==5.4.1 +kombu==5.4.2 # via celery -markupsafe==2.1.5 +markupsafe==3.0.2 # via jinja2 monotonic==1.6 # via analytics-python -mysqlclient==2.2.4 +mysqlclient==2.2.5 # via -r requirements/base.in -newrelic==9.13.0 +newrelic==10.2.0 # via edx-django-utils oauthlib==3.2.2 # via @@ -205,9 +208,9 @@ pbr==6.1.0 # via stevedore ply==3.11 # via djangoql -prompt-toolkit==3.0.47 +prompt-toolkit==3.0.48 # via click-repl -psutil==6.0.0 +psutil==6.1.0 # via edx-django-utils pycparser==2.22 # via cffi @@ -218,7 +221,7 @@ pyjwt[crypto]==2.9.0 # edx-drf-extensions # edx-rest-api-client # social-auth-core -pymongo==4.8.0 +pymongo==4.10.1 # via edx-opaque-keys pynacl==1.5.0 # via edx-django-utils @@ -237,7 +240,7 @@ pyyaml==6.0.2 # via # code-annotations # drf-spectacular -redis==5.0.8 +redis==5.2.0 # via -r requirements/base.in referencing==0.35.1 # via @@ -258,7 +261,7 @@ rpds-py==0.20.0 # referencing rules==3.5 # via -r requirements/base.in -s3transfer==0.10.2 +s3transfer==0.10.3 # via boto3 semantic-version==2.10.0 # via edx-drf-extensions @@ -267,6 +270,7 @@ simplejson==3.19.3 six==1.16.0 # via # analytics-python + # bleach # edx-auth-backends # edx-rbac # python-dateutil @@ -285,10 +289,14 @@ stevedore==5.3.0 # edx-opaque-keys text-unidecode==1.3 # via python-slugify +tinycss2==1.2.1 + # via bleach typing-extensions==4.12.2 # via edx-opaque-keys -tzdata==2024.1 - # via celery +tzdata==2024.2 + # via + # celery + # kombu uritemplate==4.1.1 # via drf-spectacular urllib3==2.2.3 @@ -302,6 +310,10 @@ vine==5.1.0 # kombu wcwidth==0.2.13 # via prompt-toolkit +webencodings==0.5.1 + # via + # bleach + # tinycss2 zipp==3.20.2 # via -r requirements/base.in diff --git a/requirements/common_constraints.txt b/requirements/common_constraints.txt index 1c4a5456..ad46ee15 100644 --- a/requirements/common_constraints.txt +++ b/requirements/common_constraints.txt @@ -34,3 +34,7 @@ elasticsearch<7.14.0 # This can be unpinned once https://github.com/openedx/edx-platform/issues/34586 # has been resolved and edx-platform is running with pymongo>=4.4.0 event-tracking<2.4.1 + +# Cause: https://github.com/openedx/edx-lint/issues/458 +# This can be unpinned once https://github.com/openedx/edx-lint/issues/459 has been resolved. +pip<24.3 diff --git a/requirements/constraints.txt b/requirements/constraints.txt index ba78494f..f4017bc5 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -21,3 +21,8 @@ backports.zoneinfo;python_version<"3.9" # path>16.14.0 has removed the deprecated abspath function, which is breaking the docs build path<16.15.0 + +# The newer version if this package has drop support for background tasks so that why +# when i include that new version (7.0.0) in the requirements upgrade, most of the jobs start failing +# so i have to pin this version to 6.1.0 +edx-django-utils==6.1.0 diff --git a/requirements/dev.txt b/requirements/dev.txt index 48f397d6..0ecd1fd3 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -15,7 +15,7 @@ asgiref==3.8.1 # -r requirements/validation.txt # django # django-cors-headers -astroid==3.2.4 +astroid==3.3.5 # via # -r requirements/validation.txt # pylint @@ -29,20 +29,22 @@ backoff==1.10.0 # via # -r requirements/validation.txt # analytics-python -billiard==4.2.0 +billiard==4.2.1 # via # -r requirements/validation.txt # celery -boto3==1.35.19 +bleach[css]==6.1.0 + # via -r requirements/validation.txt +boto3==1.35.49 # via # -r requirements/validation.txt # django-ses -botocore==1.35.19 +botocore==1.35.49 # via # -r requirements/validation.txt # boto3 # s3transfer -build==1.2.2 +build==1.2.2.post1 # via # -r requirements/pip-tools.txt # pip-tools @@ -60,7 +62,7 @@ cffi==1.17.1 # -r requirements/validation.txt # cryptography # pynacl -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via # -r requirements/validation.txt # requests @@ -98,11 +100,11 @@ code-annotations==1.8.0 # -r requirements/validation.txt # edx-lint # edx-toggles -coverage[toml]==7.6.1 +coverage[toml]==7.6.4 # via # -r requirements/validation.txt # pytest-cov -cryptography==43.0.1 +cryptography==43.0.3 # via # -r requirements/validation.txt # pyjwt @@ -120,7 +122,7 @@ diff-cover==4.0.0 # via # -c requirements/constraints.txt # -r requirements/dev.in -dill==0.3.8 +dill==0.3.9 # via # -r requirements/validation.txt # pylint @@ -158,7 +160,7 @@ django-autocomplete-light==3.11.0 # via -r requirements/validation.txt django-celery-results==2.5.1 # via -r requirements/validation.txt -django-cors-headers==4.4.0 +django-cors-headers==4.5.0 # via -r requirements/validation.txt django-crum==0.7.9 # via @@ -185,7 +187,7 @@ django-model-utils==5.0.0 # -r requirements/validation.txt # edx-celeryutils # edx-rbac -django-ses==4.1.1 +django-ses==4.2.0 # via -r requirements/validation.txt django-simple-history==3.7.0 # via -r requirements/validation.txt @@ -207,7 +209,7 @@ djangorestframework==3.15.2 # edx-drf-extensions djangorestframework-csv==3.0.2 # via -r requirements/validation.txt -dnspython==2.6.1 +dnspython==2.7.0 # via # -r requirements/validation.txt # pymongo @@ -225,19 +227,20 @@ edx-braze-client==0.2.5 # via -r requirements/validation.txt edx-celeryutils==1.3.0 # via -r requirements/validation.txt -edx-django-utils==5.15.0 +edx-django-utils==6.1.0 # via + # -c requirements/constraints.txt # -r requirements/validation.txt # edx-drf-extensions # edx-rest-api-client # edx-toggles -edx-drf-extensions==10.4.0 +edx-drf-extensions==10.5.0 # via # -r requirements/validation.txt # edx-rbac edx-i18n-tools==1.6.3 # via -r requirements/validation.txt -edx-lint==5.4.0 +edx-lint==5.4.1 # via -r requirements/validation.txt edx-opaque-keys==2.11.0 # via @@ -251,7 +254,7 @@ edx-toggles==5.2.0 # via -r requirements/validation.txt factory-boy==3.3.1 # via -r requirements/validation.txt -faker==28.4.1 +faker==30.8.1 # via # -r requirements/validation.txt # factory-boy @@ -300,11 +303,11 @@ jsonschema==4.23.0 # via # -r requirements/validation.txt # drf-spectacular -jsonschema-specifications==2023.12.1 +jsonschema-specifications==2024.10.1 # via # -r requirements/validation.txt # jsonschema -kombu==5.4.1 +kombu==5.4.2 # via # -r requirements/validation.txt # celery @@ -313,11 +316,11 @@ lxml[html-clean]==5.3.0 # -r requirements/validation.txt # edx-i18n-tools # lxml-html-clean -lxml-html-clean==0.2.2 +lxml-html-clean==0.3.1 # via # -r requirements/validation.txt # lxml -markupsafe==2.1.5 +markupsafe==3.0.2 # via # -r requirements/validation.txt # jinja2 @@ -331,9 +334,9 @@ monotonic==1.6 # analytics-python more-itertools==10.5.0 # via inflect -mysqlclient==2.2.4 +mysqlclient==2.2.5 # via -r requirements/validation.txt -newrelic==9.13.0 +newrelic==10.2.0 # via # -r requirements/validation.txt # edx-django-utils @@ -362,7 +365,7 @@ pbr==6.1.0 # stevedore pip-tools==7.4.1 # via -r requirements/pip-tools.txt -platformdirs==4.3.3 +platformdirs==4.3.6 # via # -r requirements/validation.txt # pylint @@ -379,11 +382,11 @@ polib==1.2.0 # via # -r requirements/validation.txt # edx-i18n-tools -prompt-toolkit==3.0.47 +prompt-toolkit==3.0.48 # via # -r requirements/validation.txt # click-repl -psutil==6.0.0 +psutil==6.1.0 # via # -r requirements/validation.txt # edx-django-utils @@ -405,7 +408,7 @@ pyjwt[crypto]==2.9.0 # edx-drf-extensions # edx-rest-api-client # social-auth-core -pylint==3.2.7 +pylint==3.3.1 # via # -r requirements/validation.txt # edx-lint @@ -416,7 +419,7 @@ pylint-celery==0.3 # via # -r requirements/validation.txt # edx-lint -pylint-django==2.5.5 +pylint-django==2.6.1 # via # -r requirements/validation.txt # edx-lint @@ -425,7 +428,7 @@ pylint-plugin-utils==0.8.2 # -r requirements/validation.txt # pylint-celery # pylint-django -pymongo==4.8.0 +pymongo==4.10.1 # via # -r requirements/validation.txt # edx-opaque-keys @@ -433,7 +436,7 @@ pynacl==1.5.0 # via # -r requirements/validation.txt # edx-django-utils -pyproject-hooks==1.1.0 +pyproject-hooks==1.2.0 # via # -r requirements/pip-tools.txt # build @@ -473,7 +476,7 @@ pyyaml==6.0.2 # code-annotations # drf-spectacular # edx-i18n-tools -redis==5.0.8 +redis==5.2.0 # via -r requirements/validation.txt referencing==0.35.1 # via @@ -499,7 +502,7 @@ rpds-py==0.20.0 # referencing rules==3.5 # via -r requirements/validation.txt -s3transfer==0.10.2 +s3transfer==0.10.3 # via # -r requirements/validation.txt # boto3 @@ -513,6 +516,7 @@ six==1.16.0 # via # -r requirements/validation.txt # analytics-python + # bleach # edx-auth-backends # edx-lint # edx-rbac @@ -546,21 +550,27 @@ text-unidecode==1.3 # via # -r requirements/validation.txt # python-slugify +tinycss2==1.2.1 + # via + # -r requirements/validation.txt + # bleach tomlkit==0.13.2 # via # -r requirements/validation.txt # pylint -typeguard==4.3.0 +typeguard==4.4.0 # via inflect typing-extensions==4.12.2 # via # -r requirements/validation.txt # edx-opaque-keys + # faker # typeguard -tzdata==2024.1 +tzdata==2024.2 # via # -r requirements/validation.txt # celery + # kombu uritemplate==4.1.1 # via # -r requirements/validation.txt @@ -580,6 +590,11 @@ wcwidth==0.2.13 # via # -r requirements/validation.txt # prompt-toolkit +webencodings==0.5.1 + # via + # -r requirements/validation.txt + # bleach + # tinycss2 wheel==0.44.0 # via # -r requirements/pip-tools.txt diff --git a/requirements/doc.txt b/requirements/doc.txt index f7756a80..da33ea57 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -19,7 +19,7 @@ asgiref==3.8.1 # -r requirements/test.txt # django # django-cors-headers -astroid==3.2.4 +astroid==3.3.5 # via # -r requirements/test.txt # pylint @@ -39,15 +39,17 @@ backoff==1.10.0 # analytics-python beautifulsoup4==4.12.3 # via pydata-sphinx-theme -billiard==4.2.0 +billiard==4.2.1 # via # -r requirements/test.txt # celery -boto3==1.35.19 +bleach[css]==6.1.0 + # via -r requirements/test.txt +boto3==1.35.49 # via # -r requirements/test.txt # django-ses -botocore==1.35.19 +botocore==1.35.49 # via # -r requirements/test.txt # boto3 @@ -66,7 +68,7 @@ cffi==1.17.1 # -r requirements/test.txt # cryptography # pynacl -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via # -r requirements/test.txt # requests @@ -102,11 +104,11 @@ code-annotations==1.8.0 # -r requirements/test.txt # edx-lint # edx-toggles -coverage[toml]==7.6.1 +coverage[toml]==7.6.4 # via # -r requirements/test.txt # pytest-cov -cryptography==43.0.1 +cryptography==43.0.3 # via # -r requirements/test.txt # pyjwt @@ -118,7 +120,7 @@ defusedxml==0.8.0rc2 # -r requirements/test.txt # python3-openid # social-auth-core -dill==0.3.8 +dill==0.3.9 # via # -r requirements/test.txt # pylint @@ -154,7 +156,7 @@ django-autocomplete-light==3.11.0 # via -r requirements/test.txt django-celery-results==2.5.1 # via -r requirements/test.txt -django-cors-headers==4.4.0 +django-cors-headers==4.5.0 # via -r requirements/test.txt django-crum==0.7.9 # via @@ -177,7 +179,7 @@ django-model-utils==5.0.0 # -r requirements/test.txt # edx-celeryutils # edx-rbac -django-ses==4.1.1 +django-ses==4.2.0 # via -r requirements/test.txt django-simple-history==3.7.0 # via -r requirements/test.txt @@ -199,7 +201,7 @@ djangorestframework==3.15.2 # edx-drf-extensions djangorestframework-csv==3.0.2 # via -r requirements/test.txt -dnspython==2.6.1 +dnspython==2.7.0 # via # -r requirements/test.txt # pymongo @@ -226,17 +228,18 @@ edx-braze-client==0.2.5 # via -r requirements/test.txt edx-celeryutils==1.3.0 # via -r requirements/test.txt -edx-django-utils==5.15.0 +edx-django-utils==6.1.0 # via + # -c requirements/constraints.txt # -r requirements/test.txt # edx-drf-extensions # edx-rest-api-client # edx-toggles -edx-drf-extensions==10.4.0 +edx-drf-extensions==10.5.0 # via # -r requirements/test.txt # edx-rbac -edx-lint==5.4.0 +edx-lint==5.4.1 # via -r requirements/test.txt edx-opaque-keys==2.11.0 # via @@ -250,7 +253,7 @@ edx-toggles==5.2.0 # via -r requirements/test.txt factory-boy==3.3.1 # via -r requirements/test.txt -faker==28.4.1 +faker==30.8.1 # via # -r requirements/test.txt # factory-boy @@ -292,15 +295,15 @@ jsonschema==4.23.0 # via # -r requirements/test.txt # drf-spectacular -jsonschema-specifications==2023.12.1 +jsonschema-specifications==2024.10.1 # via # -r requirements/test.txt # jsonschema -kombu==5.4.1 +kombu==5.4.2 # via # -r requirements/test.txt # celery -markupsafe==2.1.5 +markupsafe==3.0.2 # via # -r requirements/test.txt # jinja2 @@ -312,9 +315,9 @@ monotonic==1.6 # via # -r requirements/test.txt # analytics-python -mysqlclient==2.2.4 +mysqlclient==2.2.5 # via -r requirements/test.txt -newrelic==9.13.0 +newrelic==10.2.0 # via # -r requirements/test.txt # edx-django-utils @@ -328,14 +331,13 @@ oauthlib==3.2.2 packaging==24.1 # via # -r requirements/test.txt - # pydata-sphinx-theme # pytest # sphinx pbr==6.1.0 # via # -r requirements/test.txt # stevedore -platformdirs==4.3.3 +platformdirs==4.3.6 # via # -r requirements/test.txt # pylint @@ -347,11 +349,11 @@ ply==3.11 # via # -r requirements/test.txt # djangoql -prompt-toolkit==3.0.47 +prompt-toolkit==3.0.48 # via # -r requirements/test.txt # click-repl -psutil==6.0.0 +psutil==6.1.0 # via # -r requirements/test.txt # edx-django-utils @@ -359,7 +361,7 @@ pycparser==2.22 # via # -r requirements/test.txt # cffi -pydata-sphinx-theme==0.15.4 +pydata-sphinx-theme==0.16.0 # via sphinx-book-theme pygments==2.18.0 # via @@ -376,7 +378,7 @@ pyjwt[crypto]==2.9.0 # edx-drf-extensions # edx-rest-api-client # social-auth-core -pylint==3.2.7 +pylint==3.3.1 # via # -r requirements/test.txt # edx-lint @@ -387,7 +389,7 @@ pylint-celery==0.3 # via # -r requirements/test.txt # edx-lint -pylint-django==2.5.5 +pylint-django==2.6.1 # via # -r requirements/test.txt # edx-lint @@ -396,7 +398,7 @@ pylint-plugin-utils==0.8.2 # -r requirements/test.txt # pylint-celery # pylint-django -pymongo==4.8.0 +pymongo==4.10.1 # via # -r requirements/test.txt # edx-opaque-keys @@ -438,7 +440,7 @@ pyyaml==6.0.2 # drf-spectacular readme-renderer==44.0 # via -r requirements/doc.in -redis==5.0.8 +redis==5.2.0 # via -r requirements/test.txt referencing==0.35.1 # via @@ -467,7 +469,7 @@ rpds-py==0.20.0 # referencing rules==3.5 # via -r requirements/test.txt -s3transfer==0.10.2 +s3transfer==0.10.3 # via # -r requirements/test.txt # boto3 @@ -481,6 +483,7 @@ six==1.16.0 # via # -r requirements/test.txt # analytics-python + # bleach # edx-auth-backends # edx-lint # edx-rbac @@ -498,7 +501,7 @@ social-auth-core==4.5.4 # social-auth-app-django soupsieve==2.6 # via beautifulsoup4 -sphinx==8.0.2 +sphinx==8.1.3 # via # -r requirements/doc.in # pydata-sphinx-theme @@ -532,6 +535,10 @@ text-unidecode==1.3 # via # -r requirements/test.txt # python-slugify +tinycss2==1.2.1 + # via + # -r requirements/test.txt + # bleach tomlkit==0.13.2 # via # -r requirements/test.txt @@ -540,11 +547,13 @@ typing-extensions==4.12.2 # via # -r requirements/test.txt # edx-opaque-keys + # faker # pydata-sphinx-theme -tzdata==2024.1 +tzdata==2024.2 # via # -r requirements/test.txt # celery + # kombu uritemplate==4.1.1 # via # -r requirements/test.txt @@ -564,6 +573,11 @@ wcwidth==0.2.13 # via # -r requirements/test.txt # prompt-toolkit +webencodings==0.5.1 + # via + # -r requirements/test.txt + # bleach + # tinycss2 zipp==3.20.2 # via -r requirements/test.txt diff --git a/requirements/pip-tools.txt b/requirements/pip-tools.txt index e6268ff6..cf4131e2 100644 --- a/requirements/pip-tools.txt +++ b/requirements/pip-tools.txt @@ -4,7 +4,7 @@ # # make upgrade # -build==1.2.2 +build==1.2.2.post1 # via pip-tools click==8.1.7 # via pip-tools @@ -12,7 +12,7 @@ packaging==24.1 # via build pip-tools==7.4.1 # via -r requirements/pip-tools.in -pyproject-hooks==1.1.0 +pyproject-hooks==1.2.0 # via # build # pip-tools diff --git a/requirements/pip.txt b/requirements/pip.txt index 15fc7c28..a8a601eb 100644 --- a/requirements/pip.txt +++ b/requirements/pip.txt @@ -9,6 +9,8 @@ wheel==0.44.0 # The following packages are considered to be unsafe in a requirements file: pip==24.2 - # via -r requirements/pip.in -setuptools==75.0.0 + # via + # -c requirements/common_constraints.txt + # -r requirements/pip.in +setuptools==75.2.0 # via -r requirements/pip.in diff --git a/requirements/production.txt b/requirements/production.txt index 81fcb3ec..a4140c8a 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -24,15 +24,17 @@ backoff==1.10.0 # via # -r requirements/base.txt # analytics-python -billiard==4.2.0 +billiard==4.2.1 # via # -r requirements/base.txt # celery -boto3==1.35.19 +bleach[css]==6.1.0 + # via -r requirements/base.txt +boto3==1.35.49 # via # -r requirements/base.txt # django-ses -botocore==1.35.19 +botocore==1.35.49 # via # -r requirements/base.txt # boto3 @@ -51,7 +53,7 @@ cffi==1.17.1 # -r requirements/base.txt # cryptography # pynacl -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via # -r requirements/base.txt # requests @@ -80,7 +82,7 @@ code-annotations==1.8.0 # via # -r requirements/base.txt # edx-toggles -cryptography==43.0.1 +cryptography==43.0.3 # via # -r requirements/base.txt # pyjwt @@ -122,7 +124,7 @@ django-autocomplete-light==3.11.0 # via -r requirements/base.txt django-celery-results==2.5.1 # via -r requirements/base.txt -django-cors-headers==4.4.0 +django-cors-headers==4.5.0 # via -r requirements/base.txt django-crum==0.7.9 # via @@ -143,7 +145,7 @@ django-model-utils==5.0.0 # -r requirements/base.txt # edx-celeryutils # edx-rbac -django-ses==4.1.1 +django-ses==4.2.0 # via -r requirements/base.txt django-simple-history==3.7.0 # via -r requirements/base.txt @@ -165,7 +167,7 @@ djangorestframework==3.15.2 # edx-drf-extensions djangorestframework-csv==3.0.2 # via -r requirements/base.txt -dnspython==2.6.1 +dnspython==2.7.0 # via # -r requirements/base.txt # pymongo @@ -183,13 +185,14 @@ edx-braze-client==0.2.5 # via -r requirements/base.txt edx-celeryutils==1.3.0 # via -r requirements/base.txt -edx-django-utils==5.15.0 +edx-django-utils==6.1.0 # via + # -c requirements/constraints.txt # -r requirements/base.txt # edx-drf-extensions # edx-rest-api-client # edx-toggles -edx-drf-extensions==10.4.0 +edx-drf-extensions==10.5.0 # via # -r requirements/base.txt # edx-rbac @@ -203,9 +206,9 @@ edx-rest-api-client==6.0.0 # via -r requirements/base.txt edx-toggles==5.2.0 # via -r requirements/base.txt -gevent==24.2.1 +gevent==24.10.3 # via -r requirements/production.in -greenlet==3.1.0 +greenlet==3.1.1 # via gevent gunicorn==23.0.0 # via -r requirements/production.in @@ -234,15 +237,15 @@ jsonschema==4.23.0 # via # -r requirements/base.txt # drf-spectacular -jsonschema-specifications==2023.12.1 +jsonschema-specifications==2024.10.1 # via # -r requirements/base.txt # jsonschema -kombu==5.4.1 +kombu==5.4.2 # via # -r requirements/base.txt # celery -markupsafe==2.1.5 +markupsafe==3.0.2 # via # -r requirements/base.txt # jinja2 @@ -250,9 +253,9 @@ monotonic==1.6 # via # -r requirements/base.txt # analytics-python -mysqlclient==2.2.4 +mysqlclient==2.2.5 # via -r requirements/base.txt -newrelic==9.13.0 +newrelic==10.2.0 # via # -r requirements/base.txt # edx-django-utils @@ -271,11 +274,11 @@ ply==3.11 # via # -r requirements/base.txt # djangoql -prompt-toolkit==3.0.47 +prompt-toolkit==3.0.48 # via # -r requirements/base.txt # click-repl -psutil==6.0.0 +psutil==6.1.0 # via # -r requirements/base.txt # edx-django-utils @@ -293,7 +296,7 @@ pyjwt[crypto]==2.9.0 # social-auth-core pymemcache==4.0.0 # via -r requirements/production.in -pymongo==4.8.0 +pymongo==4.10.1 # via # -r requirements/base.txt # edx-opaque-keys @@ -325,7 +328,7 @@ pyyaml==6.0.2 # -r requirements/production.in # code-annotations # drf-spectacular -redis==5.0.8 +redis==5.2.0 # via -r requirements/base.txt referencing==0.35.1 # via @@ -351,7 +354,7 @@ rpds-py==0.20.0 # referencing rules==3.5 # via -r requirements/base.txt -s3transfer==0.10.2 +s3transfer==0.10.3 # via # -r requirements/base.txt # boto3 @@ -365,6 +368,7 @@ six==1.16.0 # via # -r requirements/base.txt # analytics-python + # bleach # edx-auth-backends # edx-rbac # python-dateutil @@ -391,14 +395,19 @@ text-unidecode==1.3 # via # -r requirements/base.txt # python-slugify +tinycss2==1.2.1 + # via + # -r requirements/base.txt + # bleach typing-extensions==4.12.2 # via # -r requirements/base.txt # edx-opaque-keys -tzdata==2024.1 +tzdata==2024.2 # via # -r requirements/base.txt # celery + # kombu uritemplate==4.1.1 # via # -r requirements/base.txt @@ -418,11 +427,16 @@ wcwidth==0.2.13 # via # -r requirements/base.txt # prompt-toolkit +webencodings==0.5.1 + # via + # -r requirements/base.txt + # bleach + # tinycss2 zipp==3.20.2 # via -r requirements/base.txt zope-event==5.0 # via gevent -zope-interface==7.0.3 +zope-interface==7.1.1 # via gevent # The following packages are considered to be unsafe in a requirements file: diff --git a/requirements/quality.txt b/requirements/quality.txt index f3db7f88..351fe512 100644 --- a/requirements/quality.txt +++ b/requirements/quality.txt @@ -15,7 +15,7 @@ asgiref==3.8.1 # -r requirements/base.txt # django # django-cors-headers -astroid==3.2.4 +astroid==3.3.5 # via # pylint # pylint-celery @@ -28,15 +28,17 @@ backoff==1.10.0 # via # -r requirements/base.txt # analytics-python -billiard==4.2.0 +billiard==4.2.1 # via # -r requirements/base.txt # celery -boto3==1.35.19 +bleach[css]==6.1.0 + # via -r requirements/base.txt +boto3==1.35.49 # via # -r requirements/base.txt # django-ses -botocore==1.35.19 +botocore==1.35.49 # via # -r requirements/base.txt # boto3 @@ -55,7 +57,7 @@ cffi==1.17.1 # -r requirements/base.txt # cryptography # pynacl -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via # -r requirements/base.txt # requests @@ -89,7 +91,7 @@ code-annotations==1.8.0 # -r requirements/base.txt # edx-lint # edx-toggles -cryptography==43.0.1 +cryptography==43.0.3 # via # -r requirements/base.txt # pyjwt @@ -99,7 +101,7 @@ defusedxml==0.8.0rc2 # -r requirements/base.txt # python3-openid # social-auth-core -dill==0.3.8 +dill==0.3.9 # via pylint django==4.2.16 # via @@ -133,7 +135,7 @@ django-autocomplete-light==3.11.0 # via -r requirements/base.txt django-celery-results==2.5.1 # via -r requirements/base.txt -django-cors-headers==4.4.0 +django-cors-headers==4.5.0 # via -r requirements/base.txt django-crum==0.7.9 # via @@ -154,7 +156,7 @@ django-model-utils==5.0.0 # -r requirements/base.txt # edx-celeryutils # edx-rbac -django-ses==4.1.1 +django-ses==4.2.0 # via -r requirements/base.txt django-simple-history==3.7.0 # via -r requirements/base.txt @@ -176,7 +178,7 @@ djangorestframework==3.15.2 # edx-drf-extensions djangorestframework-csv==3.0.2 # via -r requirements/base.txt -dnspython==2.6.1 +dnspython==2.7.0 # via # -r requirements/base.txt # pymongo @@ -194,17 +196,18 @@ edx-braze-client==0.2.5 # via -r requirements/base.txt edx-celeryutils==1.3.0 # via -r requirements/base.txt -edx-django-utils==5.15.0 +edx-django-utils==6.1.0 # via + # -c requirements/constraints.txt # -r requirements/base.txt # edx-drf-extensions # edx-rest-api-client # edx-toggles -edx-drf-extensions==10.4.0 +edx-drf-extensions==10.5.0 # via # -r requirements/base.txt # edx-rbac -edx-lint==5.4.0 +edx-lint==5.4.1 # via -r requirements/quality.in edx-opaque-keys==2.11.0 # via @@ -245,15 +248,15 @@ jsonschema==4.23.0 # via # -r requirements/base.txt # drf-spectacular -jsonschema-specifications==2023.12.1 +jsonschema-specifications==2024.10.1 # via # -r requirements/base.txt # jsonschema -kombu==5.4.1 +kombu==5.4.2 # via # -r requirements/base.txt # celery -markupsafe==2.1.5 +markupsafe==3.0.2 # via # -r requirements/base.txt # jinja2 @@ -263,9 +266,9 @@ monotonic==1.6 # via # -r requirements/base.txt # analytics-python -mysqlclient==2.2.4 +mysqlclient==2.2.5 # via -r requirements/base.txt -newrelic==9.13.0 +newrelic==10.2.0 # via # -r requirements/base.txt # edx-django-utils @@ -278,17 +281,17 @@ pbr==6.1.0 # via # -r requirements/base.txt # stevedore -platformdirs==4.3.3 +platformdirs==4.3.6 # via pylint ply==3.11 # via # -r requirements/base.txt # djangoql -prompt-toolkit==3.0.47 +prompt-toolkit==3.0.48 # via # -r requirements/base.txt # click-repl -psutil==6.0.0 +psutil==6.1.0 # via # -r requirements/base.txt # edx-django-utils @@ -308,7 +311,7 @@ pyjwt[crypto]==2.9.0 # edx-drf-extensions # edx-rest-api-client # social-auth-core -pylint==3.2.7 +pylint==3.3.1 # via # edx-lint # pylint-celery @@ -316,13 +319,13 @@ pylint==3.2.7 # pylint-plugin-utils pylint-celery==0.3 # via edx-lint -pylint-django==2.5.5 +pylint-django==2.6.1 # via edx-lint pylint-plugin-utils==0.8.2 # via # pylint-celery # pylint-django -pymongo==4.8.0 +pymongo==4.10.1 # via # -r requirements/base.txt # edx-opaque-keys @@ -351,7 +354,7 @@ pyyaml==6.0.2 # -r requirements/base.txt # code-annotations # drf-spectacular -redis==5.0.8 +redis==5.2.0 # via -r requirements/base.txt referencing==0.35.1 # via @@ -377,7 +380,7 @@ rpds-py==0.20.0 # referencing rules==3.5 # via -r requirements/base.txt -s3transfer==0.10.2 +s3transfer==0.10.3 # via # -r requirements/base.txt # boto3 @@ -391,6 +394,7 @@ six==1.16.0 # via # -r requirements/base.txt # analytics-python + # bleach # edx-auth-backends # edx-lint # edx-rbac @@ -420,16 +424,21 @@ text-unidecode==1.3 # via # -r requirements/base.txt # python-slugify +tinycss2==1.2.1 + # via + # -r requirements/base.txt + # bleach tomlkit==0.13.2 # via pylint typing-extensions==4.12.2 # via # -r requirements/base.txt # edx-opaque-keys -tzdata==2024.1 +tzdata==2024.2 # via # -r requirements/base.txt # celery + # kombu uritemplate==4.1.1 # via # -r requirements/base.txt @@ -449,6 +458,11 @@ wcwidth==0.2.13 # via # -r requirements/base.txt # prompt-toolkit +webencodings==0.5.1 + # via + # -r requirements/base.txt + # bleach + # tinycss2 zipp==3.20.2 # via -r requirements/base.txt diff --git a/requirements/test.txt b/requirements/test.txt index fb49889f..80e4876e 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -15,7 +15,7 @@ asgiref==3.8.1 # -r requirements/base.txt # django # django-cors-headers -astroid==3.2.4 +astroid==3.3.5 # via # pylint # pylint-celery @@ -28,15 +28,17 @@ backoff==1.10.0 # via # -r requirements/base.txt # analytics-python -billiard==4.2.0 +billiard==4.2.1 # via # -r requirements/base.txt # celery -boto3==1.35.19 +bleach[css]==6.1.0 + # via -r requirements/base.txt +boto3==1.35.49 # via # -r requirements/base.txt # django-ses -botocore==1.35.19 +botocore==1.35.49 # via # -r requirements/base.txt # boto3 @@ -55,7 +57,7 @@ cffi==1.17.1 # -r requirements/base.txt # cryptography # pynacl -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via # -r requirements/base.txt # requests @@ -90,11 +92,11 @@ code-annotations==1.8.0 # -r requirements/test.in # edx-lint # edx-toggles -coverage[toml]==7.6.1 +coverage[toml]==7.6.4 # via # -r requirements/test.in # pytest-cov -cryptography==43.0.1 +cryptography==43.0.3 # via # -r requirements/base.txt # pyjwt @@ -106,7 +108,7 @@ defusedxml==0.8.0rc2 # -r requirements/base.txt # python3-openid # social-auth-core -dill==0.3.8 +dill==0.3.9 # via pylint django==4.2.16 # via @@ -140,7 +142,7 @@ django-autocomplete-light==3.11.0 # via -r requirements/base.txt django-celery-results==2.5.1 # via -r requirements/base.txt -django-cors-headers==4.4.0 +django-cors-headers==4.5.0 # via -r requirements/base.txt django-crum==0.7.9 # via @@ -163,7 +165,7 @@ django-model-utils==5.0.0 # -r requirements/base.txt # edx-celeryutils # edx-rbac -django-ses==4.1.1 +django-ses==4.2.0 # via -r requirements/base.txt django-simple-history==3.7.0 # via -r requirements/base.txt @@ -185,7 +187,7 @@ djangorestframework==3.15.2 # edx-drf-extensions djangorestframework-csv==3.0.2 # via -r requirements/base.txt -dnspython==2.6.1 +dnspython==2.7.0 # via # -r requirements/base.txt # pymongo @@ -203,17 +205,18 @@ edx-braze-client==0.2.5 # via -r requirements/base.txt edx-celeryutils==1.3.0 # via -r requirements/base.txt -edx-django-utils==5.15.0 +edx-django-utils==6.1.0 # via + # -c requirements/constraints.txt # -r requirements/base.txt # edx-drf-extensions # edx-rest-api-client # edx-toggles -edx-drf-extensions==10.4.0 +edx-drf-extensions==10.5.0 # via # -r requirements/base.txt # edx-rbac -edx-lint==5.4.0 +edx-lint==5.4.1 # via -r requirements/test.in edx-opaque-keys==2.11.0 # via @@ -227,7 +230,7 @@ edx-toggles==5.2.0 # via -r requirements/base.txt factory-boy==3.3.1 # via -r requirements/test.in -faker==28.4.1 +faker==30.8.1 # via factory-boy freezegun==1.5.1 # via -r requirements/test.in @@ -260,15 +263,15 @@ jsonschema==4.23.0 # via # -r requirements/base.txt # drf-spectacular -jsonschema-specifications==2023.12.1 +jsonschema-specifications==2024.10.1 # via # -r requirements/base.txt # jsonschema -kombu==5.4.1 +kombu==5.4.2 # via # -r requirements/base.txt # celery -markupsafe==2.1.5 +markupsafe==3.0.2 # via # -r requirements/base.txt # jinja2 @@ -278,9 +281,9 @@ monotonic==1.6 # via # -r requirements/base.txt # analytics-python -mysqlclient==2.2.4 +mysqlclient==2.2.5 # via -r requirements/base.txt -newrelic==9.13.0 +newrelic==10.2.0 # via # -r requirements/base.txt # edx-django-utils @@ -295,7 +298,7 @@ pbr==6.1.0 # via # -r requirements/base.txt # stevedore -platformdirs==4.3.3 +platformdirs==4.3.6 # via pylint pluggy==1.5.0 # via pytest @@ -303,11 +306,11 @@ ply==3.11 # via # -r requirements/base.txt # djangoql -prompt-toolkit==3.0.47 +prompt-toolkit==3.0.48 # via # -r requirements/base.txt # click-repl -psutil==6.0.0 +psutil==6.1.0 # via # -r requirements/base.txt # edx-django-utils @@ -323,7 +326,7 @@ pyjwt[crypto]==2.9.0 # edx-drf-extensions # edx-rest-api-client # social-auth-core -pylint==3.2.7 +pylint==3.3.1 # via # edx-lint # pylint-celery @@ -331,13 +334,13 @@ pylint==3.2.7 # pylint-plugin-utils pylint-celery==0.3 # via edx-lint -pylint-django==2.5.5 +pylint-django==2.6.1 # via edx-lint pylint-plugin-utils==0.8.2 # via # pylint-celery # pylint-django -pymongo==4.8.0 +pymongo==4.10.1 # via # -r requirements/base.txt # edx-opaque-keys @@ -376,7 +379,7 @@ pyyaml==6.0.2 # -r requirements/base.txt # code-annotations # drf-spectacular -redis==5.0.8 +redis==5.2.0 # via -r requirements/base.txt referencing==0.35.1 # via @@ -402,7 +405,7 @@ rpds-py==0.20.0 # referencing rules==3.5 # via -r requirements/base.txt -s3transfer==0.10.2 +s3transfer==0.10.3 # via # -r requirements/base.txt # boto3 @@ -416,6 +419,7 @@ six==1.16.0 # via # -r requirements/base.txt # analytics-python + # bleach # edx-auth-backends # edx-lint # edx-rbac @@ -443,16 +447,22 @@ text-unidecode==1.3 # via # -r requirements/base.txt # python-slugify +tinycss2==1.2.1 + # via + # -r requirements/base.txt + # bleach tomlkit==0.13.2 # via pylint typing-extensions==4.12.2 # via # -r requirements/base.txt # edx-opaque-keys -tzdata==2024.1 + # faker +tzdata==2024.2 # via # -r requirements/base.txt # celery + # kombu uritemplate==4.1.1 # via # -r requirements/base.txt @@ -472,6 +482,11 @@ wcwidth==0.2.13 # via # -r requirements/base.txt # prompt-toolkit +webencodings==0.5.1 + # via + # -r requirements/base.txt + # bleach + # tinycss2 zipp==3.20.2 # via -r requirements/base.txt diff --git a/requirements/validation.txt b/requirements/validation.txt index b30b4b4e..f16f03d0 100644 --- a/requirements/validation.txt +++ b/requirements/validation.txt @@ -19,7 +19,7 @@ asgiref==3.8.1 # -r requirements/test.txt # django # django-cors-headers -astroid==3.2.4 +astroid==3.3.5 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -36,17 +36,21 @@ backoff==1.10.0 # -r requirements/quality.txt # -r requirements/test.txt # analytics-python -billiard==4.2.0 +billiard==4.2.1 # via # -r requirements/quality.txt # -r requirements/test.txt # celery -boto3==1.35.19 +bleach[css]==6.1.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt +boto3==1.35.49 # via # -r requirements/quality.txt # -r requirements/test.txt # django-ses -botocore==1.35.19 +botocore==1.35.49 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -69,7 +73,7 @@ cffi==1.17.1 # -r requirements/test.txt # cryptography # pynacl -charset-normalizer==3.3.2 +charset-normalizer==3.4.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -112,11 +116,11 @@ code-annotations==1.8.0 # -r requirements/test.txt # edx-lint # edx-toggles -coverage[toml]==7.6.1 +coverage[toml]==7.6.4 # via # -r requirements/test.txt # pytest-cov -cryptography==43.0.1 +cryptography==43.0.3 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -130,7 +134,7 @@ defusedxml==0.8.0rc2 # -r requirements/test.txt # python3-openid # social-auth-core -dill==0.3.8 +dill==0.3.9 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -173,7 +177,7 @@ django-celery-results==2.5.1 # via # -r requirements/quality.txt # -r requirements/test.txt -django-cors-headers==4.4.0 +django-cors-headers==4.5.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -208,7 +212,7 @@ django-model-utils==5.0.0 # -r requirements/test.txt # edx-celeryutils # edx-rbac -django-ses==4.1.1 +django-ses==4.2.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -240,7 +244,7 @@ djangorestframework-csv==3.0.2 # via # -r requirements/quality.txt # -r requirements/test.txt -dnspython==2.6.1 +dnspython==2.7.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -270,21 +274,22 @@ edx-celeryutils==1.3.0 # via # -r requirements/quality.txt # -r requirements/test.txt -edx-django-utils==5.15.0 +edx-django-utils==6.1.0 # via + # -c requirements/constraints.txt # -r requirements/quality.txt # -r requirements/test.txt # edx-drf-extensions # edx-rest-api-client # edx-toggles -edx-drf-extensions==10.4.0 +edx-drf-extensions==10.5.0 # via # -r requirements/quality.txt # -r requirements/test.txt # edx-rbac edx-i18n-tools==1.6.3 # via -r requirements/validation.in -edx-lint==5.4.0 +edx-lint==5.4.1 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -307,7 +312,7 @@ edx-toggles==5.2.0 # -r requirements/test.txt factory-boy==3.3.1 # via -r requirements/test.txt -faker==28.4.1 +faker==30.8.1 # via # -r requirements/test.txt # factory-boy @@ -353,12 +358,12 @@ jsonschema==4.23.0 # -r requirements/quality.txt # -r requirements/test.txt # drf-spectacular -jsonschema-specifications==2023.12.1 +jsonschema-specifications==2024.10.1 # via # -r requirements/quality.txt # -r requirements/test.txt # jsonschema -kombu==5.4.1 +kombu==5.4.2 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -367,9 +372,9 @@ lxml[html-clean,html_clean]==5.3.0 # via # edx-i18n-tools # lxml-html-clean -lxml-html-clean==0.2.2 +lxml-html-clean==0.3.1 # via lxml -markupsafe==2.1.5 +markupsafe==3.0.2 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -384,11 +389,11 @@ monotonic==1.6 # -r requirements/quality.txt # -r requirements/test.txt # analytics-python -mysqlclient==2.2.4 +mysqlclient==2.2.5 # via # -r requirements/quality.txt # -r requirements/test.txt -newrelic==9.13.0 +newrelic==10.2.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -414,7 +419,7 @@ pbr==6.1.0 # -r requirements/quality.txt # -r requirements/test.txt # stevedore -platformdirs==4.3.3 +platformdirs==4.3.6 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -430,12 +435,12 @@ ply==3.11 # djangoql polib==1.2.0 # via edx-i18n-tools -prompt-toolkit==3.0.47 +prompt-toolkit==3.0.48 # via # -r requirements/quality.txt # -r requirements/test.txt # click-repl -psutil==6.0.0 +psutil==6.1.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -458,7 +463,7 @@ pyjwt[crypto]==2.9.0 # edx-drf-extensions # edx-rest-api-client # social-auth-core -pylint==3.2.7 +pylint==3.3.1 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -471,7 +476,7 @@ pylint-celery==0.3 # -r requirements/quality.txt # -r requirements/test.txt # edx-lint -pylint-django==2.5.5 +pylint-django==2.6.1 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -482,7 +487,7 @@ pylint-plugin-utils==0.8.2 # -r requirements/test.txt # pylint-celery # pylint-django -pymongo==4.8.0 +pymongo==4.10.1 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -531,7 +536,7 @@ pyyaml==6.0.2 # code-annotations # drf-spectacular # edx-i18n-tools -redis==5.0.8 +redis==5.2.0 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -565,7 +570,7 @@ rules==3.5 # via # -r requirements/quality.txt # -r requirements/test.txt -s3transfer==0.10.2 +s3transfer==0.10.3 # via # -r requirements/quality.txt # -r requirements/test.txt @@ -584,6 +589,7 @@ six==1.16.0 # -r requirements/quality.txt # -r requirements/test.txt # analytics-python + # bleach # edx-auth-backends # edx-lint # edx-rbac @@ -621,6 +627,11 @@ text-unidecode==1.3 # -r requirements/quality.txt # -r requirements/test.txt # python-slugify +tinycss2==1.2.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # bleach tomlkit==0.13.2 # via # -r requirements/quality.txt @@ -631,11 +642,13 @@ typing-extensions==4.12.2 # -r requirements/quality.txt # -r requirements/test.txt # edx-opaque-keys -tzdata==2024.1 + # faker +tzdata==2024.2 # via # -r requirements/quality.txt # -r requirements/test.txt # celery + # kombu uritemplate==4.1.1 # via # -r requirements/quality.txt @@ -659,6 +672,12 @@ wcwidth==0.2.13 # -r requirements/quality.txt # -r requirements/test.txt # prompt-toolkit +webencodings==0.5.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # bleach + # tinycss2 zipp==3.20.2 # via # -r requirements/quality.txt