Skip to content

Commit

Permalink
Merge pull request #208 from edx/dsheraz/PROD-1211/transcript-credent…
Browse files Browse the repository at this point in the history
…ials-models

add transcript credentials model
  • Loading branch information
DawoudSheraz authored Mar 27, 2020
2 parents eef1ecb + 2ebd862 commit 2317310
Show file tree
Hide file tree
Showing 12 changed files with 207 additions and 63 deletions.
10 changes: 10 additions & 0 deletions edxval/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
EncodedVideo,
Profile,
ThirdPartyTranscriptCredentialsState,
TranscriptCredentials,
TranscriptPreference,
Video,
VideoImage,
Expand Down Expand Up @@ -113,6 +114,14 @@ class TranscriptPreferenceAdmin(admin.ModelAdmin):
model = TranscriptPreference


class TranscriptCredentialsAdmin(admin.ModelAdmin):
"""
Admin for TranscriptCredentials model.
"""
model = TranscriptCredentials
exclude = ('api_key', 'api_secret')


class ThirdPartyTranscriptCredentialsStateAdmin(admin.ModelAdmin):
""" Admin for ThirdPartyTranscriptCredentialsState """
list_display = ('org', 'provider', 'has_creds', 'created', 'modified')
Expand All @@ -125,6 +134,7 @@ class ThirdPartyTranscriptCredentialsStateAdmin(admin.ModelAdmin):
admin.site.register(Profile, ProfileAdmin)
admin.site.register(Video, VideoAdmin)
admin.site.register(VideoTranscript, VideoTranscriptAdmin)
admin.site.register(TranscriptCredentials, TranscriptCredentialsAdmin)
admin.site.register(TranscriptPreference, TranscriptPreferenceAdmin)
admin.site.register(VideoImage, VideoImageAdmin)
admin.site.register(CourseVideo, CourseVideoAdmin)
Expand Down
37 changes: 37 additions & 0 deletions edxval/migrations/0016_add_transcript_credentials_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-03-25 07:41
from __future__ import unicode_literals

from django.db import migrations, models
import django.utils.timezone
import fernet_fields.fields
import model_utils.fields


class Migration(migrations.Migration):

dependencies = [
('edxval', '0015_remove_thirdpartytranscriptcredentialsstate_exists'),
]

operations = [
migrations.CreateModel(
name='TranscriptCredentials',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
('org', models.CharField(help_text='This value must match the value of organization in studio/edx-platform.', max_length=50, verbose_name='Organization')),
('provider', models.CharField(choices=[('Custom', 'Custom'), ('3PlayMedia', '3PlayMedia'), ('Cielo24', 'Cielo24')], max_length=50, verbose_name='Transcript provider')),
('api_key', fernet_fields.fields.EncryptedTextField(max_length=255, verbose_name='API key')),
('api_secret', fernet_fields.fields.EncryptedTextField(max_length=255, verbose_name='API secret')),
],
options={
'verbose_name_plural': 'Transcript Credentials',
},
),
migrations.AlterUniqueTogether(
name='transcriptcredentials',
unique_together=set([('org', 'provider')]),
),
]
22 changes: 22 additions & 0 deletions edxval/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from django.dispatch import receiver
from django.urls import reverse
from django.utils.six import python_2_unicode_compatible
from fernet_fields import EncryptedTextField
from model_utils.models import TimeStampedModel

from edxval.utils import (
Expand Down Expand Up @@ -687,6 +688,27 @@ def __str__(self):
)


class TranscriptCredentials(TimeStampedModel):
"""
Model to contain third party transcription service provider preferences.
"""
org = models.CharField(
'Organization',
max_length=50,
help_text='This value must match the value of organization in studio/edx-platform.'
)
provider = models.CharField('Transcript provider', max_length=50, choices=TranscriptProviderType.CHOICES)
api_key = EncryptedTextField('API key', max_length=255)
api_secret = EncryptedTextField('API secret', max_length=255)

class Meta:
unique_together = ('org', 'provider')
verbose_name_plural = 'Transcript Credentials'

def __str__(self):
return '{org} - {provider}'.format(org=self.org, provider=self.provider)


@receiver(models.signals.post_save, sender=Video)
def video_status_update_callback(sender, **kwargs): # pylint: disable=unused-argument
"""
Expand Down
3 changes: 3 additions & 0 deletions edxval/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,6 @@
VIDEO_TRANSCRIPTS_MAX_BYTES=3145728, # 3 MB
DIRECTORY_PREFIX='video-transcripts/',
)

# Set this value in the environment-specific files (e.g. local.py, production.py, test.py)
FERNET_KEYS = ['insecure-ferent-key']
42 changes: 40 additions & 2 deletions edxval/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
""" Test for models """
from __future__ import absolute_import

from django.test import TestCase
from cryptography.fernet import InvalidToken
from django.test import TestCase, override_settings

from edxval.models import Video, VideoTranscript
from edxval.models import TranscriptCredentials, TranscriptProviderType, Video, VideoTranscript
from edxval.tests import constants
from edxval.utils import invalidate_fernet_cached_properties


class VideoTranscriptTest(TestCase):
Expand Down Expand Up @@ -35,3 +37,39 @@ def test_filename_property_new_line(self):

self.assertNotIn('\n', video_trancript.filename)
assert str(video_trancript) == "en Transcript for new-line-not-allowed"


class TranscriptCredentialsTest(TestCase):
"""
Test suite for TranscriptCredentials model.
"""
def setUp(self):
super(TranscriptCredentialsTest, self).setUp()
self.credentials_data = {
'org': 'MAx',
'provider': TranscriptProviderType.CIELO24,
'api_key': 'test-key',
'api_secret': 'test-secret'
}
TranscriptCredentials.objects.create(**self.credentials_data)

def test_decryption(self):
"""
Verify that with unchanged encryption key, the decrypted data will be same as before encryption.
"""
credentials = TranscriptCredentials.objects.get(
org=self.credentials_data['org'], provider=self.credentials_data['provider']
)
self.assertEqual(credentials.api_key, self.credentials_data['api_key'])
self.assertEqual(credentials.api_secret, self.credentials_data['api_secret'])

def test_decryption_with_new_key(self):
"""
Verify that with a new encryption key, previously encrypted data cannot be decrypted.
"""
invalidate_fernet_cached_properties(TranscriptCredentials, ['api_key', 'api_secret'])
with override_settings(FERNET_KEYS=['new-key']):
with self.assertRaises(InvalidToken):
TranscriptCredentials.objects.get(
org=self.credentials_data['org'], provider=self.credentials_data['provider']
)
18 changes: 18 additions & 0 deletions edxval/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,3 +247,21 @@ def validate_generated_images(value, max_items):
raise ValidationError(u'list must only contain strings.')

return value


def invalidate_fernet_cached_properties(model, fields):
"""
Invalidates transcript credential fernet field's cached properties.
Arguments:
model (class): Model class containing fernet fields.
fields (list): A list of fernet fields whose cache is to be invalidated.
"""
for field_name in fields:
try:
field = model._meta.get_field(field_name)
del field.keys
del field.fernet_keys
del field.fernet
except AttributeError:
pass
1 change: 1 addition & 0 deletions requirements/base.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

boto
Django>=1.11
django-fernet-fields
django-model-utils
edx-drf-extensions
-e git+https://github.com/edx/django-rest-framework-oauth.git@0a43e8525f1e3048efe4bc70c03de308a277197c#egg=djangorestframework-oauth==1.1.1
Expand Down
49 changes: 27 additions & 22 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,37 +11,40 @@ astroid==2.3.3 # via pylint, pylint-celery
atomicwrites==1.3.0 # via pytest
attrs==19.3.0 # via pytest
backports.functools-lru-cache==1.6.1 # via caniusepython3
bleach==3.1.1 # via readme-renderer
bleach==3.1.3 # via readme-renderer
boto==2.49.0 # via -r requirements/base.in
caniusepython3==7.2.0 # via -r requirements/quality.in
certifi==2019.11.28 # via requests
cffi==1.14.0 # via cryptography
chardet==3.0.4 # via pysrt, requests
click-log==0.3.2 # via edx-lint
click==7.0 # via click-log, edx-lint, pip-tools
coverage==5.0.3 # via -r requirements/test.in, coveralls, pytest-cov
click==7.1.1 # via click-log, edx-lint, pip-tools
coverage==5.0.4 # via -r requirements/test.in, coveralls, pytest-cov
coveralls==1.11.1 # via -r requirements/travis.in
ddt==1.2.2 # via -r requirements/test.in
cryptography==2.8 # via django-fernet-fields
ddt==1.3.1 # via -r requirements/test.in
diff-cover==2.6.0 # via -r requirements/dev.in
distlib==0.3.0 # via caniusepython3, virtualenv
django-fernet-fields==0.6 # via -r requirements/base.in
django-model-utils==3.2.0 # via -c requirements/constraints.txt, -r requirements/base.in
django-storages==1.9.1 # via -r requirements/base.in
django-waffle==0.20.0 # via edx-django-utils, edx-drf-extensions
django==1.11.28 # via -r requirements/base.in, django-model-utils, django-storages, edx-django-utils, edx-drf-extensions, rest-condition
djangorestframework-jwt==1.11.0 # via edx-drf-extensions
djangorestframework==3.9.4 # via -c requirements/constraints.txt, edx-drf-extensions, rest-condition
django==2.2.11 # via -r requirements/base.in, django-fernet-fields, django-model-utils, django-storages, drf-jwt, edx-django-utils, edx-drf-extensions, rest-condition
djangorestframework==3.9.4 # via -c requirements/constraints.txt, drf-jwt, edx-drf-extensions, rest-condition
docopt==0.6.2 # via coveralls
docutils==0.16 # via readme-renderer
edx-django-utils==3.0 # via edx-drf-extensions
edx-drf-extensions==3.0.0 # via -r requirements/base.in
drf-jwt==1.14.0 # via edx-drf-extensions
edx-django-utils==3.1 # via edx-drf-extensions
edx-drf-extensions==5.0.2 # via -r requirements/base.in
edx-lint==1.4.1 # via -r requirements/quality.in
edx-opaque-keys==2.0.1 # via edx-drf-extensions
enum34==1.1.9 # via -r requirements/base.in
enum34==1.1.10 # via -r requirements/base.in
filelock==3.0.12 # via tox, virtualenv
fs==2.4.11 # via -r requirements/test.in
future==0.18.2 # via pyjwkest
idna==2.9 # via requests
importlib-metadata==1.5.0 # via importlib-resources, inflect, pluggy, pytest, tox, virtualenv
importlib-resources==1.2.0 # via virtualenv
importlib-resources==1.4.0 # via virtualenv
inflect==3.0.2 # via jinja2-pluralize
isort==4.3.21 # via -r requirements/quality.in, pylint
jinja2-pluralize==0.3.0 # via diff-cover
Expand All @@ -52,21 +55,22 @@ markupsafe==1.1.1 # via jinja2
mccabe==0.6.1 # via pylint
mock==3.0.5 # via -r requirements/test.in
more-itertools==5.0.0 # via -c requirements/constraints.txt, pytest, zipp
newrelic==5.8.0.136 # via edx-django-utils
packaging==20.1 # via caniusepython3, pytest, tox
newrelic==5.10.0.138 # via edx-django-utils
packaging==20.3 # via caniusepython3, pytest, tox
pathlib2==2.3.5 # via pytest
pbr==5.4.4 # via stevedore
pip-tools==4.5.1 # via -r requirements/dev.in
pkginfo==1.5.0.1 # via twine
pluggy==0.13.1 # via diff-cover, pytest, tox
psutil==1.2.1 # via edx-django-utils, edx-drf-extensions
psutil==1.2.1 # via edx-django-utils
py==1.8.1 # via pytest, tox
pycodestyle==2.5.0 # via -r requirements/quality.in
pycparser==2.20 # via cffi
pycryptodomex==3.9.7 # via pyjwkest
pydocstyle==5.0.2 # via -r requirements/quality.in
pygments==2.5.2 # via diff-cover, readme-renderer
pyjwkest==1.3.2 # via edx-drf-extensions
pyjwt==1.7.1 # via djangorestframework-jwt
pygments==2.6.1 # via diff-cover, readme-renderer
pyjwkest==1.4.2 # via edx-drf-extensions
pyjwt==1.7.1 # via drf-jwt
pylint-celery==0.3 # via edx-lint
pylint-django==2.0.11 # via edx-lint
pylint-plugin-utils==0.6 # via pylint-celery, pylint-django
Expand All @@ -79,24 +83,25 @@ pytest-django==3.8.0 # via -r requirements/test.in
pytest==4.6.9 # via -c requirements/constraints.txt, pytest-cov, pytest-django
python-dateutil==2.8.1 # via edx-drf-extensions
pytz==2019.3 # via django, fs
readme-renderer==24.0 # via twine
readme-renderer==25.0 # via twine
requests-toolbelt==0.9.1 # via twine
requests==2.23.0 # via caniusepython3, coveralls, edx-drf-extensions, pyjwkest, requests-toolbelt, twine
rest-condition==1.0.3 # via edx-drf-extensions
semantic-version==2.8.4 # via edx-drf-extensions
six==1.14.0 # via astroid, bleach, diff-cover, django-waffle, edx-drf-extensions, edx-lint, edx-opaque-keys, fs, mock, more-itertools, packaging, pathlib2, pip-tools, pyjwkest, pytest, python-dateutil, readme-renderer, stevedore, tox, virtualenv
six==1.14.0 # via astroid, bleach, cryptography, diff-cover, django-waffle, edx-drf-extensions, edx-lint, edx-opaque-keys, fs, mock, more-itertools, packaging, pathlib2, pip-tools, pyjwkest, pytest, python-dateutil, readme-renderer, stevedore, tox, virtualenv
snowballstemmer==2.0.0 # via pydocstyle
sqlparse==0.3.1 # via django
stevedore==1.32.0 # via edx-opaque-keys
toml==0.10.0 # via tox
tox-battery==0.5.2 # via -r requirements/travis.in
tox==3.14.5 # via -r requirements/travis.in, tox-battery
tox==3.14.6 # via -r requirements/travis.in, tox-battery
tqdm==4.43.0 # via twine
twine==1.15.0 # via -r requirements/quality.in
typed-ast==1.4.1 # via astroid
typing==3.7.4.1 # via fs
urllib3==1.25.8 # via requests
virtualenv==20.0.7 # via tox
wcwidth==0.1.8 # via pytest
virtualenv==20.0.14 # via tox
wcwidth==0.1.9 # via pytest
webencodings==0.5.1 # via bleach
wrapt==1.11.2 # via astroid
zipp==1.0.0 # via -c requirements/constraints.txt, importlib-metadata, importlib-resources
Expand Down
Loading

0 comments on commit 2317310

Please sign in to comment.