diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8b1fd2258..abfdc8615 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -17,6 +17,10 @@ Unreleased ---------- * nothing unreleased +[4.23.12] +---------- +* feat: feat: added encrypted client id and secret for canvas integration + [4.23.11] ---------- * feat: implement back-off and retry for SAP SuccessFactors diff --git a/enterprise/__init__.py b/enterprise/__init__.py index 6e2a0a543..4a57fd2db 100644 --- a/enterprise/__init__.py +++ b/enterprise/__init__.py @@ -2,4 +2,4 @@ Your project description goes here. """ -__version__ = "4.23.11" +__version__ = "4.23.12" diff --git a/enterprise/signals.py b/enterprise/signals.py index d16c4ea4d..7725b72f1 100644 --- a/enterprise/signals.py +++ b/enterprise/signals.py @@ -455,6 +455,17 @@ def generate_default_orchestration_record_display_name(sender, instance, **kwarg instance.display_name = f'SSO-config-{instance.identity_provider}-{num_records_for_customer + 1}' +@receiver(pre_save, sender=CanvasEnterpriseCustomerConfiguration) +def mirror_id_and_secret_to_decrypted_fields(sender, instance, **kwargs): # pylint: disable=unused-argument + """ + Mirror the client_id and client_secret to the decrypted fields. + """ + if instance.client_id != instance.decrypted_client_id: + instance.decrypted_client_id = instance.client_id + if instance.client_secret != instance.decrypted_client_secret: + instance.decrypted_client_secret = instance.client_secret + + # Don't connect this receiver if we dont have access to CourseEnrollment model if CourseEnrollment is not None: post_save.connect(create_enterprise_enrollment_receiver, sender=CourseEnrollment) diff --git a/integrated_channels/canvas/migrations/0036_canvasenterprisecustomerconfiguration_decrypted_client_id_and_more.py b/integrated_channels/canvas/migrations/0036_canvasenterprisecustomerconfiguration_decrypted_client_id_and_more.py new file mode 100644 index 000000000..d5957022b --- /dev/null +++ b/integrated_channels/canvas/migrations/0036_canvasenterprisecustomerconfiguration_decrypted_client_id_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.15 on 2024-08-26 07:03 + +from django.db import migrations +import fernet_fields.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('canvas', '0035_canvaslearnerassessmentdatatransmissionaudit_is_transmitted_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='canvasenterprisecustomerconfiguration', + name='decrypted_client_id', + field=fernet_fields.fields.EncryptedCharField(blank=True, default='', help_text='The encrypted API Client ID provided to edX by the enterprise customer to be used to make API calls to Canvas on behalf of the customer. It will be encrypted when stored in the database.', max_length=255, null=True, verbose_name='Encrypted API Client ID'), + ), + migrations.AddField( + model_name='canvasenterprisecustomerconfiguration', + name='decrypted_client_secret', + field=fernet_fields.fields.EncryptedCharField(blank=True, default='', help_text='The encrypted API Client Secret provided to edX by the enterprise customer to be used to make API calls to Canvas on behalf of the customer. It will be encrypted when stored in the database.', max_length=255, null=True, verbose_name='Encrypted API Client Secret'), + ), + ] diff --git a/integrated_channels/canvas/models.py b/integrated_channels/canvas/models.py index 791e1b6af..ef5c55f6a 100644 --- a/integrated_channels/canvas/models.py +++ b/integrated_channels/canvas/models.py @@ -6,6 +6,7 @@ import uuid from logging import getLogger +from fernet_fields import EncryptedCharField from six.moves.urllib.parse import urljoin from django.conf import settings @@ -44,6 +45,18 @@ class CanvasEnterpriseCustomerConfiguration(EnterpriseCustomerPluginConfiguratio ) ) + decrypted_client_id = EncryptedCharField( + max_length=255, + blank=True, + default='', + verbose_name="Encrypted API Client ID", + help_text=( + "The encrypted API Client ID provided to edX by the enterprise customer to be used to make API " + "calls to Canvas on behalf of the customer. It will be encrypted when stored in the database." + ), + null=True, + ) + client_secret = models.CharField( max_length=255, blank=True, @@ -55,6 +68,18 @@ class CanvasEnterpriseCustomerConfiguration(EnterpriseCustomerPluginConfiguratio ) ) + decrypted_client_secret = EncryptedCharField( + max_length=255, + blank=True, + default='', + verbose_name="Encrypted API Client Secret", + help_text=( + "The encrypted API Client Secret provided to edX by the enterprise customer to be used to make " + " API calls to Canvas on behalf of the customer. It will be encrypted when stored in the database." + ), + null=True, + ) + canvas_account_id = models.BigIntegerField( null=True, blank=True,