Merge pull request #16489 from edx/revert-16479-revert-16201-transcript-secure-credentials

Revert "Revert "Transcript secure credentials""
This commit is contained in:
Muzaffar yousaf
2017-11-09 15:57:36 +05:00
committed by GitHub
33 changed files with 1465 additions and 201 deletions

View File

@@ -0,0 +1,9 @@
"""
Django admin for Video Pipeline models.
"""
from config_models.admin import ConfigurationModelAdmin
from django.contrib import admin
from openedx.core.djangoapps.video_pipeline.models import VideoPipelineIntegration
admin.site.register(VideoPipelineIntegration, ConfigurationModelAdmin)

View File

@@ -0,0 +1,51 @@
"""
API utils in order to communicate to edx-video-pipeline.
"""
import json
import logging
from django.core.exceptions import ObjectDoesNotExist
from slumber.exceptions import HttpClientError
from openedx.core.djangoapps.video_pipeline.models import VideoPipelineIntegration
from openedx.core.djangoapps.video_pipeline.utils import create_video_pipeline_api_client
log = logging.getLogger(__name__)
def update_3rd_party_transcription_service_credentials(**credentials_payload):
"""
Updates the 3rd party transcription service's credentials.
Arguments:
credentials_payload(dict): A payload containing org, provider and its credentials.
Returns:
A Boolean specifying whether the credentials were updated or not
and an error response received from pipeline.
"""
error_response, is_updated = {}, False
pipeline_integration = VideoPipelineIntegration.current()
if pipeline_integration.enabled:
try:
video_pipeline_user = pipeline_integration.get_service_user()
except ObjectDoesNotExist:
return error_response, is_updated
client = create_video_pipeline_api_client(user=video_pipeline_user, api_url=pipeline_integration.api_url)
try:
client.transcript_credentials.post(credentials_payload)
is_updated = True
except HttpClientError as ex:
is_updated = False
log.exception(
('[video-pipeline-service] Unable to update transcript credentials '
'-- org=%s -- provider=%s -- response=%s.'),
credentials_payload.get('org'),
credentials_payload.get('provider'),
ex.content,
)
error_response = json.loads(ex.content)
return error_response, is_updated

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='VideoPipelineIntegration',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
('api_url', models.URLField(help_text='edx-video-pipeline API URL.', verbose_name='Internal API URL')),
('service_username', models.CharField(default=b'video_pipeline_service_user', help_text='Username created for Video Pipeline Integration, e.g. video_pipeline_service_user.', max_length=100)),
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
],
options={
'ordering': ('-change_date',),
'abstract': False,
},
),
]

View File

@@ -0,0 +1,31 @@
"""
Model to hold edx-video-pipeline configurations.
"""
from config_models.models import ConfigurationModel
from django.contrib.auth import get_user_model
from django.db import models
from django.utils.translation import ugettext_lazy as _
class VideoPipelineIntegration(ConfigurationModel):
"""
Manages configuration for connecting to the edx-video-pipeline service and using its API.
"""
api_url = models.URLField(
verbose_name=_('Internal API URL'),
help_text=_('edx-video-pipeline API URL.')
)
service_username = models.CharField(
max_length=100,
default='video_pipeline_service_user',
null=False,
blank=False,
help_text=_('Username created for Video Pipeline Integration, e.g. video_pipeline_service_user.')
)
def get_service_user(self):
# NOTE: We load the user model here to avoid issues at startup time that result from the hacks
# in lms/startup.py.
User = get_user_model() # pylint: disable=invalid-name
return User.objects.get(username=self.service_username)

View File

@@ -0,0 +1,23 @@
"""
Mixins to test video pipeline integration.
"""
from openedx.core.djangoapps.video_pipeline.models import VideoPipelineIntegration
class VideoPipelineIntegrationMixin(object):
"""
Utility for working with the video pipeline service during testing.
"""
video_pipeline_integration_defaults = {
'enabled': True,
'api_url': 'https://video-pipeline.example.com/api/v1/',
'service_username': 'cms_video_pipeline_service_user',
}
def create_video_pipeline_integration(self, **kwargs):
"""
Creates a new `VideoPipelineIntegration` record with `video_pipeline_integration_defaults`,
and it can be updated with any provided overrides.
"""
fields = dict(self.video_pipeline_integration_defaults, **kwargs)
return VideoPipelineIntegration.objects.create(**fields)

View File

@@ -0,0 +1,99 @@
"""
Tests for Video Pipeline api utils.
"""
import ddt
import json
from mock import Mock, patch
from django.test.testcases import TestCase
from slumber.exceptions import HttpClientError
from student.tests.factories import UserFactory
from openedx.core.djangoapps.video_pipeline.api import update_3rd_party_transcription_service_credentials
from openedx.core.djangoapps.video_pipeline.tests.mixins import VideoPipelineIntegrationMixin
@ddt.ddt
class TestAPIUtils(VideoPipelineIntegrationMixin, TestCase):
"""
Tests for API Utils.
"""
def setUp(self):
self.pipeline_integration = self.create_video_pipeline_integration()
self.user = UserFactory(username=self.pipeline_integration.service_username)
def test_update_transcription_service_credentials_with_integration_disabled(self):
"""
Test updating the credentials when service integration is disabled.
"""
self.pipeline_integration.enabled = False
self.pipeline_integration.save()
__, is_updated = update_3rd_party_transcription_service_credentials()
self.assertFalse(is_updated)
def test_update_transcription_service_credentials_with_unknown_user(self):
"""
Test updating the credentials when expected service user is not registered.
"""
self.pipeline_integration.service_username = 'non_existent_user'
self.pipeline_integration.save()
__, is_updated = update_3rd_party_transcription_service_credentials()
self.assertFalse(is_updated)
@ddt.data(
{
'username': 'Jason_cielo_24',
'api_key': '12345678',
},
{
'api_key': '12345678',
'api_secret': '11111111',
}
)
@patch('openedx.core.djangoapps.video_pipeline.api.log')
@patch('openedx.core.djangoapps.video_pipeline.utils.EdxRestApiClient')
def test_update_transcription_service_credentials(self, credentials_payload, mock_client, mock_logger):
"""
Tests that the update transcription service credentials api util works as expected.
"""
# Mock the post request
mock_credentials_endpoint = mock_client.return_value.transcript_credentials
# Try updating the transcription service credentials
error_response, is_updated = update_3rd_party_transcription_service_credentials(**credentials_payload)
mock_credentials_endpoint.post.assert_called_with(credentials_payload)
# Making sure log.exception is not called.
self.assertDictEqual(error_response, {})
self.assertFalse(mock_logger.exception.called)
self.assertTrue(is_updated)
@patch('openedx.core.djangoapps.video_pipeline.api.log')
@patch('openedx.core.djangoapps.video_pipeline.utils.EdxRestApiClient')
def test_update_transcription_service_credentials_exceptions(self, mock_client, mock_logger):
"""
Tests that the update transcription service credentials logs the exception occurring
during communication with edx-video-pipeline.
"""
error_content = '{"error_type": "1"}'
# Mock the post request
mock_credentials_endpoint = mock_client.return_value.transcript_credentials
mock_credentials_endpoint.post = Mock(side_effect=HttpClientError(content=error_content))
# try updating the transcription service credentials
credentials_payload = {
'org': 'mit',
'provider': 'ABC Provider',
'api_key': '61c56a8d0'
}
error_response, is_updated = update_3rd_party_transcription_service_credentials(**credentials_payload)
mock_credentials_endpoint.post.assert_called_with(credentials_payload)
# Assert the results.
self.assertFalse(is_updated)
self.assertDictEqual(error_response, json.loads(error_content))
mock_logger.exception.assert_called_with(
'[video-pipeline-service] Unable to update transcript credentials -- org=%s -- provider=%s -- response=%s.',
credentials_payload['org'],
credentials_payload['provider'],
error_content
)

View File

@@ -0,0 +1,19 @@
from django.conf import settings
from edx_rest_api_client.client import EdxRestApiClient
from openedx.core.lib.token_utils import JwtBuilder
def create_video_pipeline_api_client(user, api_url):
"""
Returns an API client which can be used to make Video Pipeline API requests.
Arguments:
user(User): A requesting user.
api_url(unicode): It is video pipeline's API URL.
"""
jwt_token = JwtBuilder(user).build_token(
scopes=[],
expires_in=settings.OAUTH_ID_TOKEN_EXPIRATION
)
return EdxRestApiClient(api_url, jwt=jwt_token)