From 5eebfbedb699450682b650fdfa9fd262bf9dd1b4 Mon Sep 17 00:00:00 2001 From: "mueez.khan" Date: Wed, 11 Oct 2023 14:27:00 +0500 Subject: [PATCH 01/11] feat: added fields for holding encrypted data in database for blackboard --- .../migrations/0018_auto_20231011_0915.py | 48 +++++++++++++ integrated_channels/blackboard/models.py | 68 +++++++++++++++++++ integrated_channels/utils.py | 9 +++ 3 files changed, 125 insertions(+) create mode 100644 integrated_channels/blackboard/migrations/0018_auto_20231011_0915.py diff --git a/integrated_channels/blackboard/migrations/0018_auto_20231011_0915.py b/integrated_channels/blackboard/migrations/0018_auto_20231011_0915.py new file mode 100644 index 0000000000..c98de8d335 --- /dev/null +++ b/integrated_channels/blackboard/migrations/0018_auto_20231011_0915.py @@ -0,0 +1,48 @@ +# Generated by Django 3.2.20 on 2023-10-11 09:15 + +from django.db import migrations +from integrated_channels.utils import dummy_reverse +import fernet_fields.fields + + +def populate_decrypted_fields(apps, schema_editor): + """ + Populates the encryption fields with the data previously stored in database. + """ + BlackboardEnterpriseCustomerConfiguration = apps.get_model('blackboard', 'BlackboardEnterpriseCustomerConfiguration') + + for blackboard_enterprise_configuration in BlackboardEnterpriseCustomerConfiguration.objects.all(): + blackboard_enterprise_configuration.decrypted_client_id = blackboard_enterprise_configuration.client_id + blackboard_enterprise_configuration.decrypted_client_secret = blackboard_enterprise_configuration.client_secret + blackboard_enterprise_configuration.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('blackboard', '0017_alter_historicalblackboardenterprisecustomerconfiguration_options'), + ] + + operations = [ + migrations.AddField( + model_name='blackboardenterprisecustomerconfiguration', + name='decrypted_client_id', + field=fernet_fields.fields.EncryptedCharField(blank=True, default='', help_text='The API Client ID (encrypted at db level) provided to edX by the enterprise customer to be used to make API calls to Degreed on behalf of the customer.', max_length=255, verbose_name='API Client ID encrypted at db level'), + ), + migrations.AddField( + model_name='blackboardenterprisecustomerconfiguration', + name='decrypted_client_secret', + field=fernet_fields.fields.EncryptedCharField(blank=True, default='', help_text='The API Client Secret (encrypted at db level) provided to edX by the enterprise customer to be used to make API calls to Degreed on behalf of the customer.', max_length=255, verbose_name='API Client Secret encrypted at db level'), + ), + migrations.AddField( + model_name='historicalblackboardenterprisecustomerconfiguration', + name='decrypted_client_id', + field=fernet_fields.fields.EncryptedCharField(blank=True, default='', help_text='The API Client ID (encrypted at db level) provided to edX by the enterprise customer to be used to make API calls to Degreed on behalf of the customer.', max_length=255, verbose_name='API Client ID encrypted at db level'), + ), + migrations.AddField( + model_name='historicalblackboardenterprisecustomerconfiguration', + name='decrypted_client_secret', + field=fernet_fields.fields.EncryptedCharField(blank=True, default='', help_text='The API Client Secret (encrypted at db level) provided to edX by the enterprise customer to be used to make API calls to Degreed on behalf of the customer.', max_length=255, verbose_name='API Client Secret encrypted at db level'), + ), + migrations.RunPython(populate_decrypted_fields, dummy_reverse), + ] diff --git a/integrated_channels/blackboard/models.py b/integrated_channels/blackboard/models.py index b0c613cf35..716d3e8fa1 100644 --- a/integrated_channels/blackboard/models.py +++ b/integrated_channels/blackboard/models.py @@ -7,11 +7,13 @@ from logging import getLogger from config_models.models import ConfigurationModel +from fernet_fields import EncryptedCharField from simple_history.models import HistoricalRecords from six.moves.urllib.parse import urljoin from django.conf import settings from django.db import models +from django.utils.encoding import force_bytes, force_str from integrated_channels.blackboard.exporters.content_metadata import BlackboardContentMetadataExporter from integrated_channels.blackboard.exporters.learner_data import BlackboardLearnerExporter @@ -109,6 +111,39 @@ class BlackboardEnterpriseCustomerConfiguration(EnterpriseCustomerPluginConfigur ) ) + decrypted_client_id = EncryptedCharField( + max_length=255, + blank=True, + default='', + verbose_name="API Client ID encrypted at db level", + help_text=( + "The API Client ID (encrypted at db level) provided to edX by the enterprise customer to be used" + " to make API calls to Degreed on behalf of the customer." + ) + ) + + @property + def encrypted_client_id(self): + """ + Return encrypted client_id as a string. + The data is encrypted in the DB at rest, but is unencrypted in the app when retrieved through the + decrypted_client_id field. This method will encrypt the client_id again before sending. + """ + if self.decrypted_client_id: + return force_str( + self._meta.get_field('decrypted_client_id').fernet.encrypt( + force_bytes(self.decrypted_client_id) + ) + ) + return self.decrypted_client_id + + @encrypted_client_id.setter + def encrypted_client_id(self, value): + """ + Set the encrypted client_id. + """ + self.decrypted_client_id = value + client_secret = models.CharField( max_length=255, blank=True, @@ -120,6 +155,39 @@ class BlackboardEnterpriseCustomerConfiguration(EnterpriseCustomerPluginConfigur ) ) + decrypted_client_secret = EncryptedCharField( + max_length=255, + blank=True, + default='', + verbose_name="API Client Secret encrypted at db level", + help_text=( + "The API Client Secret (encrypted at db level) provided to edX by the enterprise customer to be " + "used to make API calls to Degreed on behalf of the customer." + ), + ) + + @property + def encrypted_client_secret(self): + """ + Return encrypted client_secret as a string. + The data is encrypted in the DB at rest, but is unencrypted in the app when retrieved through the + decrypted_client_secret field. This method will encrypt the client_secret again before sending. + """ + if self.decrypted_client_secret: + return force_str( + self._meta.get_field('decrypted_client_secret').fernet.encrypt( + force_bytes(self.decrypted_client_secret) + ) + ) + return self.decrypted_client_secret + + @encrypted_client_secret.setter + def encrypted_client_secret(self, value): + """ + Set the encrypted client_secret. + """ + self.decrypted_client_secret = value + blackboard_base_url = models.CharField( max_length=255, blank=True, diff --git a/integrated_channels/utils.py b/integrated_channels/utils.py index b41c4b55f9..e140194291 100644 --- a/integrated_channels/utils.py +++ b/integrated_channels/utils.py @@ -489,3 +489,12 @@ def get_enterprise_client_by_channel_code(channel_code): 'canvas': CanvasAPIClient, } return _enterprise_client_model_by_channel_code[channel_code] + + +def dummy_reverse(_apps, _schema_editor): + """ + Reverse a data migration but do nothing. + :param _apps: + :param _schema_editor: + :return: + """ From 313b04bee452551486ae4da50100f2d8d9f64c00 Mon Sep 17 00:00:00 2001 From: MueezKhan246 Date: Mon, 22 Apr 2024 17:39:05 +0500 Subject: [PATCH 02/11] refactor: removing conflict issues from utils file --- integrated_channels/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/integrated_channels/utils.py b/integrated_channels/utils.py index c241d8d23a..e60e5b9953 100644 --- a/integrated_channels/utils.py +++ b/integrated_channels/utils.py @@ -510,7 +510,6 @@ def get_enterprise_client_by_channel_code(channel_code): return _enterprise_client_model_by_channel_code[channel_code] -<<<<<<< HEAD def dummy_reverse(_apps, _schema_editor): """ Reverse a data migration but do nothing. @@ -518,7 +517,9 @@ def dummy_reverse(_apps, _schema_editor): :param _schema_editor: :return: """ -======= + pass + + def stringify_and_store_api_record( enterprise_customer, enterprise_customer_configuration_id, @@ -571,4 +572,3 @@ def stringify_and_store_api_record( f"data={data}" ) return data ->>>>>>> 5648d58dde396760dc447a449b6320af9a3889df From 4ddea10b20ffed37c61c17fa5f23630b457a588a Mon Sep 17 00:00:00 2001 From: MueezKhan246 Date: Mon, 22 Apr 2024 17:58:55 +0500 Subject: [PATCH 03/11] refactor: removed unused import --- integrated_channels/blackboard/models.py | 1 - 1 file changed, 1 deletion(-) diff --git a/integrated_channels/blackboard/models.py b/integrated_channels/blackboard/models.py index 760bb4ee18..dfa914b5c5 100644 --- a/integrated_channels/blackboard/models.py +++ b/integrated_channels/blackboard/models.py @@ -8,7 +8,6 @@ from config_models.models import ConfigurationModel from fernet_fields import EncryptedCharField -from simple_history.models import HistoricalRecords from six.moves.urllib.parse import urljoin from django.conf import settings From 5f3cb0851546deba3c69f3eec37d66fe9868bef4 Mon Sep 17 00:00:00 2001 From: MueezKhan246 Date: Mon, 22 Apr 2024 21:15:41 +0500 Subject: [PATCH 04/11] refactor: removing unnecessary pass statement --- integrated_channels/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/integrated_channels/utils.py b/integrated_channels/utils.py index e60e5b9953..9dc18fd433 100644 --- a/integrated_channels/utils.py +++ b/integrated_channels/utils.py @@ -517,7 +517,6 @@ def dummy_reverse(_apps, _schema_editor): :param _schema_editor: :return: """ - pass def stringify_and_store_api_record( From 4f1aa45fb1c06a2009a04863a3b133ec46d945ec Mon Sep 17 00:00:00 2001 From: MueezKhan246 Date: Tue, 23 Apr 2024 15:59:28 +0500 Subject: [PATCH 05/11] refactor: resolved migration issues --- .../migrations/0018_auto_20231011_0915.py | 48 ------------------- .../migrations/0020_auto_20240422_1709.py | 25 ++++++++++ .../migrations/0021_auto_20240423_1057.py | 26 ++++++++++ integrated_channels/utils.py | 9 ---- 4 files changed, 51 insertions(+), 57 deletions(-) delete mode 100644 integrated_channels/blackboard/migrations/0018_auto_20231011_0915.py create mode 100644 integrated_channels/blackboard/migrations/0020_auto_20240422_1709.py create mode 100644 integrated_channels/blackboard/migrations/0021_auto_20240423_1057.py diff --git a/integrated_channels/blackboard/migrations/0018_auto_20231011_0915.py b/integrated_channels/blackboard/migrations/0018_auto_20231011_0915.py deleted file mode 100644 index c98de8d335..0000000000 --- a/integrated_channels/blackboard/migrations/0018_auto_20231011_0915.py +++ /dev/null @@ -1,48 +0,0 @@ -# Generated by Django 3.2.20 on 2023-10-11 09:15 - -from django.db import migrations -from integrated_channels.utils import dummy_reverse -import fernet_fields.fields - - -def populate_decrypted_fields(apps, schema_editor): - """ - Populates the encryption fields with the data previously stored in database. - """ - BlackboardEnterpriseCustomerConfiguration = apps.get_model('blackboard', 'BlackboardEnterpriseCustomerConfiguration') - - for blackboard_enterprise_configuration in BlackboardEnterpriseCustomerConfiguration.objects.all(): - blackboard_enterprise_configuration.decrypted_client_id = blackboard_enterprise_configuration.client_id - blackboard_enterprise_configuration.decrypted_client_secret = blackboard_enterprise_configuration.client_secret - blackboard_enterprise_configuration.save() - - -class Migration(migrations.Migration): - - dependencies = [ - ('blackboard', '0017_alter_historicalblackboardenterprisecustomerconfiguration_options'), - ] - - operations = [ - migrations.AddField( - model_name='blackboardenterprisecustomerconfiguration', - name='decrypted_client_id', - field=fernet_fields.fields.EncryptedCharField(blank=True, default='', help_text='The API Client ID (encrypted at db level) provided to edX by the enterprise customer to be used to make API calls to Degreed on behalf of the customer.', max_length=255, verbose_name='API Client ID encrypted at db level'), - ), - migrations.AddField( - model_name='blackboardenterprisecustomerconfiguration', - name='decrypted_client_secret', - field=fernet_fields.fields.EncryptedCharField(blank=True, default='', help_text='The API Client Secret (encrypted at db level) provided to edX by the enterprise customer to be used to make API calls to Degreed on behalf of the customer.', max_length=255, verbose_name='API Client Secret encrypted at db level'), - ), - migrations.AddField( - model_name='historicalblackboardenterprisecustomerconfiguration', - name='decrypted_client_id', - field=fernet_fields.fields.EncryptedCharField(blank=True, default='', help_text='The API Client ID (encrypted at db level) provided to edX by the enterprise customer to be used to make API calls to Degreed on behalf of the customer.', max_length=255, verbose_name='API Client ID encrypted at db level'), - ), - migrations.AddField( - model_name='historicalblackboardenterprisecustomerconfiguration', - name='decrypted_client_secret', - field=fernet_fields.fields.EncryptedCharField(blank=True, default='', help_text='The API Client Secret (encrypted at db level) provided to edX by the enterprise customer to be used to make API calls to Degreed on behalf of the customer.', max_length=255, verbose_name='API Client Secret encrypted at db level'), - ), - migrations.RunPython(populate_decrypted_fields, dummy_reverse), - ] diff --git a/integrated_channels/blackboard/migrations/0020_auto_20240422_1709.py b/integrated_channels/blackboard/migrations/0020_auto_20240422_1709.py new file mode 100644 index 0000000000..4afd0928ca --- /dev/null +++ b/integrated_channels/blackboard/migrations/0020_auto_20240422_1709.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2.23 on 2024-04-22 17:09 + +from django.db import migrations +import fernet_fields.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('blackboard', '0019_delete_historicalblackboardenterprisecustomerconfiguration'), + ] + + operations = [ + migrations.AddField( + model_name='blackboardenterprisecustomerconfiguration', + name='decrypted_client_id', + field=fernet_fields.fields.EncryptedCharField(blank=True, default='', help_text='The API Client ID (encrypted at db level) provided to edX by the enterprise customer to be used to make API calls to Degreed on behalf of the customer.', max_length=255, verbose_name='API Client ID encrypted at db level'), + ), + migrations.AddField( + model_name='blackboardenterprisecustomerconfiguration', + name='decrypted_client_secret', + field=fernet_fields.fields.EncryptedCharField(blank=True, default='', help_text='The API Client Secret (encrypted at db level) provided to edX by the enterprise customer to be used to make API calls to Degreed on behalf of the customer.', max_length=255, verbose_name='API Client Secret encrypted at db level'), + ), + ] + diff --git a/integrated_channels/blackboard/migrations/0021_auto_20240423_1057.py b/integrated_channels/blackboard/migrations/0021_auto_20240423_1057.py new file mode 100644 index 0000000000..75a498655d --- /dev/null +++ b/integrated_channels/blackboard/migrations/0021_auto_20240423_1057.py @@ -0,0 +1,26 @@ +# Generated by Django 3.2.23 on 2024-04-23 10:57 + +from django.db import migrations + + +def populate_decrypted_fields(apps, schema_editor): + """ + Populates the encryption fields with the data previously stored in database. + """ + BlackboardEnterpriseCustomerConfiguration = apps.get_model('blackboard', 'BlackboardEnterpriseCustomerConfiguration') + + for blackboard_enterprise_configuration in BlackboardEnterpriseCustomerConfiguration.objects.all(): + blackboard_enterprise_configuration.decrypted_client_id = blackboard_enterprise_configuration.client_id + blackboard_enterprise_configuration.decrypted_client_secret = blackboard_enterprise_configuration.client_secret + blackboard_enterprise_configuration.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('blackboard', '0020_auto_20240422_1709'), + ] + + operations = [ + migrations.RunPython(populate_decrypted_fields, reverse_code=migrations.RunPython.noop), + ] diff --git a/integrated_channels/utils.py b/integrated_channels/utils.py index 9dc18fd433..879043a9e0 100644 --- a/integrated_channels/utils.py +++ b/integrated_channels/utils.py @@ -510,15 +510,6 @@ def get_enterprise_client_by_channel_code(channel_code): return _enterprise_client_model_by_channel_code[channel_code] -def dummy_reverse(_apps, _schema_editor): - """ - Reverse a data migration but do nothing. - :param _apps: - :param _schema_editor: - :return: - """ - - def stringify_and_store_api_record( enterprise_customer, enterprise_customer_configuration_id, From 48b4f06b890c3a9278db6832db6f85c9f2a7ac2b Mon Sep 17 00:00:00 2001 From: MueezKhan246 Date: Tue, 23 Apr 2024 16:32:04 +0500 Subject: [PATCH 06/11] refactor: removed redundant line at the end of file --- .../blackboard/migrations/0020_auto_20240422_1709.py | 1 - 1 file changed, 1 deletion(-) diff --git a/integrated_channels/blackboard/migrations/0020_auto_20240422_1709.py b/integrated_channels/blackboard/migrations/0020_auto_20240422_1709.py index 4afd0928ca..49b53579bb 100644 --- a/integrated_channels/blackboard/migrations/0020_auto_20240422_1709.py +++ b/integrated_channels/blackboard/migrations/0020_auto_20240422_1709.py @@ -22,4 +22,3 @@ class Migration(migrations.Migration): field=fernet_fields.fields.EncryptedCharField(blank=True, default='', help_text='The API Client Secret (encrypted at db level) provided to edX by the enterprise customer to be used to make API calls to Degreed on behalf of the customer.', max_length=255, verbose_name='API Client Secret encrypted at db level'), ), ] - From 6a4813b168f07356d95e169f1227abb9b50ecd6a Mon Sep 17 00:00:00 2001 From: MueezKhan246 Date: Thu, 2 May 2024 17:11:49 +0500 Subject: [PATCH 07/11] test: updating test case for blackboard global config model --- .../api/v1/blackboard/serializers.py | 5 ++++ .../migrations/0021_auto_20240423_1057.py | 15 ++--------- integrated_channels/blackboard/utils.py | 17 +++++++++++++ test_utils/factories.py | 2 ++ .../test_api/test_blackboard/test_views.py | 25 +++++++++++++++++++ 5 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 integrated_channels/blackboard/utils.py diff --git a/integrated_channels/api/v1/blackboard/serializers.py b/integrated_channels/api/v1/blackboard/serializers.py index fc40e2dabb..01c13a597c 100644 --- a/integrated_channels/api/v1/blackboard/serializers.py +++ b/integrated_channels/api/v1/blackboard/serializers.py @@ -18,6 +18,8 @@ class Meta: extra_fields = ( 'client_id', 'client_secret', + 'encrypted_client_id', + 'encrypted_client_secret', 'blackboard_base_url', 'refresh_token', 'uuid', @@ -25,6 +27,9 @@ class Meta: ) fields = EnterpriseCustomerPluginConfigSerializer.Meta.fields + extra_fields + encrypted_client_id = serializers.CharField(required=False, allow_blank=False, read_only=False) + encrypted_client_secret = serializers.CharField(required=False, allow_blank=False, read_only=False) + class BlackboardGlobalConfigSerializer(serializers.ModelSerializer): diff --git a/integrated_channels/blackboard/migrations/0021_auto_20240423_1057.py b/integrated_channels/blackboard/migrations/0021_auto_20240423_1057.py index 75a498655d..c5fa684682 100644 --- a/integrated_channels/blackboard/migrations/0021_auto_20240423_1057.py +++ b/integrated_channels/blackboard/migrations/0021_auto_20240423_1057.py @@ -1,18 +1,7 @@ # Generated by Django 3.2.23 on 2024-04-23 10:57 from django.db import migrations - - -def populate_decrypted_fields(apps, schema_editor): - """ - Populates the encryption fields with the data previously stored in database. - """ - BlackboardEnterpriseCustomerConfiguration = apps.get_model('blackboard', 'BlackboardEnterpriseCustomerConfiguration') - - for blackboard_enterprise_configuration in BlackboardEnterpriseCustomerConfiguration.objects.all(): - blackboard_enterprise_configuration.decrypted_client_id = blackboard_enterprise_configuration.client_id - blackboard_enterprise_configuration.decrypted_client_secret = blackboard_enterprise_configuration.client_secret - blackboard_enterprise_configuration.save() +from integrated_channels.blackboard.utils import populate_decrypted_fields_blackboard class Migration(migrations.Migration): @@ -22,5 +11,5 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RunPython(populate_decrypted_fields, reverse_code=migrations.RunPython.noop), + migrations.RunPython(populate_decrypted_fields_blackboard, reverse_code=migrations.RunPython.noop), ] diff --git a/integrated_channels/blackboard/utils.py b/integrated_channels/blackboard/utils.py new file mode 100644 index 0000000000..d96549de5b --- /dev/null +++ b/integrated_channels/blackboard/utils.py @@ -0,0 +1,17 @@ +""" +Utilities for Blackboard integrated channels. +""" + + +def populate_decrypted_fields_blackboard(apps, schema_editor=None): + """ + Populates the encryption fields in Blackboard config with the data previously stored in database. + """ + BlackboardEnterpriseCustomerConfiguration = apps.get_model( + 'blackboard', 'BlackboardEnterpriseCustomerConfiguration' + ) + + for blackboard_enterprise_configuration in BlackboardEnterpriseCustomerConfiguration.objects.all(): + blackboard_enterprise_configuration.decrypted_client_id = blackboard_enterprise_configuration.client_id + blackboard_enterprise_configuration.decrypted_client_secret = blackboard_enterprise_configuration.client_secret + blackboard_enterprise_configuration.save() diff --git a/test_utils/factories.py b/test_utils/factories.py index 16ff12f2d4..c25eb1e6b7 100644 --- a/test_utils/factories.py +++ b/test_utils/factories.py @@ -891,6 +891,8 @@ class Meta: blackboard_base_url = factory.LazyAttribute(lambda x: FAKER.url()) client_id = factory.LazyAttribute(lambda x: FAKER.random_int(min=1)) client_secret = factory.LazyAttribute(lambda x: FAKER.uuid4()) + decrypted_client_id = factory.LazyAttribute(lambda x: FAKER.random_int(min=1)) + decrypted_client_secret = factory.LazyAttribute(lambda x: FAKER.uuid4()) refresh_token = factory.LazyAttribute(lambda x: FAKER.uuid4()) diff --git a/tests/test_integrated_channels/test_api/test_blackboard/test_views.py b/tests/test_integrated_channels/test_api/test_blackboard/test_views.py index 848b73eb64..73dc5355dd 100644 --- a/tests/test_integrated_channels/test_api/test_blackboard/test_views.py +++ b/tests/test_integrated_channels/test_api/test_blackboard/test_views.py @@ -4,11 +4,13 @@ import json from unittest import mock +from django.apps import apps from django.urls import reverse from enterprise.constants import ENTERPRISE_ADMIN_ROLE from enterprise.utils import localized_utcnow from integrated_channels.blackboard.models import BlackboardEnterpriseCustomerConfiguration +from integrated_channels.blackboard.utils import populate_decrypted_fields_blackboard from test_utils import FAKE_UUIDS, APITest, factories @@ -129,6 +131,8 @@ def test_update(self, mock_current_request): payload = { 'client_secret': 1000, 'client_id': 1001, + 'encrypted_client_secret': 1000, + 'encrypted_client_id': 1001, 'blackboard_base_url': 'http://testing2', 'enterprise_customer': FAKE_UUIDS[0], } @@ -136,8 +140,29 @@ def test_update(self, mock_current_request): self.enterprise_customer_conf.refresh_from_db() self.assertEqual(self.enterprise_customer_conf.client_secret, '1000') self.assertEqual(self.enterprise_customer_conf.client_id, '1001') + self.assertEqual(self.enterprise_customer_conf.decrypted_client_secret, '1000') + self.assertEqual(self.enterprise_customer_conf.decrypted_client_id, '1001') self.assertEqual(self.enterprise_customer_conf.blackboard_base_url, 'http://testing2') self.assertEqual(response.status_code, 200) + + @mock.patch('enterprise.rules.crum.get_current_request') + def test_populate_decrypted_fields(self, mock_current_request): + mock_current_request.return_value = self.get_request_with_jwt_cookie( + system_wide_role=ENTERPRISE_ADMIN_ROLE, + context=self.enterprise_customer.uuid, + ) + url = reverse('api:v1:blackboard:configuration-detail', args=[self.enterprise_customer_conf.id]) + client_secret = self.enterprise_customer_conf.client_secret + payload = { + 'encrypted_client_secret': '1000', + 'enterprise_customer': FAKE_UUIDS[0], + } + self.client.put(url, payload) + self.enterprise_customer_conf.refresh_from_db() + self.assertEqual(self.enterprise_customer_conf.decrypted_client_secret, '1000') + populate_decrypted_fields_blackboard(apps) + self.enterprise_customer_conf.refresh_from_db() + self.assertEqual(self.enterprise_customer_conf.decrypted_client_secret, client_secret) @mock.patch('enterprise.rules.crum.get_current_request') def test_partial_update(self, mock_current_request): From 44eb27accf8134c12fe0dc7ed1b870cc566834ba Mon Sep 17 00:00:00 2001 From: MueezKhan246 Date: Thu, 2 May 2024 17:28:00 +0500 Subject: [PATCH 08/11] refactor: removing trailing space --- .../test_api/test_blackboard/test_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_integrated_channels/test_api/test_blackboard/test_views.py b/tests/test_integrated_channels/test_api/test_blackboard/test_views.py index 73dc5355dd..effdca2004 100644 --- a/tests/test_integrated_channels/test_api/test_blackboard/test_views.py +++ b/tests/test_integrated_channels/test_api/test_blackboard/test_views.py @@ -144,7 +144,7 @@ def test_update(self, mock_current_request): self.assertEqual(self.enterprise_customer_conf.decrypted_client_id, '1001') self.assertEqual(self.enterprise_customer_conf.blackboard_base_url, 'http://testing2') self.assertEqual(response.status_code, 200) - + @mock.patch('enterprise.rules.crum.get_current_request') def test_populate_decrypted_fields(self, mock_current_request): mock_current_request.return_value = self.get_request_with_jwt_cookie( From 8ab0a56b2e7696dd80f65adb0887128e1da90e00 Mon Sep 17 00:00:00 2001 From: MueezKhan246 Date: Thu, 2 May 2024 17:54:01 +0500 Subject: [PATCH 09/11] refactor: disabling lint warning for unused schema editor --- integrated_channels/blackboard/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrated_channels/blackboard/utils.py b/integrated_channels/blackboard/utils.py index d96549de5b..fef020de67 100644 --- a/integrated_channels/blackboard/utils.py +++ b/integrated_channels/blackboard/utils.py @@ -3,7 +3,7 @@ """ -def populate_decrypted_fields_blackboard(apps, schema_editor=None): +def populate_decrypted_fields_blackboard(apps, schema_editor=None): # pylint: unused-argument """ Populates the encryption fields in Blackboard config with the data previously stored in database. """ From 585970180d1dedfea6e764dd3c65b8cece0c22d0 Mon Sep 17 00:00:00 2001 From: MueezKhan246 Date: Thu, 2 May 2024 18:12:00 +0500 Subject: [PATCH 10/11] refactor: disabling unused-argument --- integrated_channels/blackboard/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrated_channels/blackboard/utils.py b/integrated_channels/blackboard/utils.py index fef020de67..7d9ea39cb6 100644 --- a/integrated_channels/blackboard/utils.py +++ b/integrated_channels/blackboard/utils.py @@ -3,7 +3,7 @@ """ -def populate_decrypted_fields_blackboard(apps, schema_editor=None): # pylint: unused-argument +def populate_decrypted_fields_blackboard(apps, schema_editor=None): # pylint: disable=unused-argument """ Populates the encryption fields in Blackboard config with the data previously stored in database. """ From 4d6eb66690439d6535ae96fa6e6f2331ee82c883 Mon Sep 17 00:00:00 2001 From: MueezKhan246 Date: Tue, 7 May 2024 19:37:26 +0500 Subject: [PATCH 11/11] refactor: updated changelog file --- CHANGELOG.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 15533e97c3..acbb70f6f8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -17,6 +17,10 @@ Unreleased ---------- * nothing unreleased +[4.17.2] +-------- +* feat: added fields for holding encrypted data in database for blackboard + [4.17.1] -------- * revert: revert async task functionality implemented in 4.15.6