Skip to content

Commit

Permalink
feat: added fields for holding encrypted data in database for blackboard
Browse files Browse the repository at this point in the history
  • Loading branch information
MueezKhan246 committed Oct 11, 2023
1 parent 99a4958 commit 5eebfbe
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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),
]
68 changes: 68 additions & 0 deletions integrated_channels/blackboard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
9 changes: 9 additions & 0 deletions integrated_channels/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
"""

0 comments on commit 5eebfbe

Please sign in to comment.