Commit bf3ecc67 by Douglas Hall Committed by Douglas Hall

Revert "Initialize enterprise api client with provided user"

parent ab713181
...@@ -30,8 +30,8 @@ from openedx.core.lib.api.permissions import ApiKeyHeaderPermission, ApiKeyHeade ...@@ -30,8 +30,8 @@ from openedx.core.lib.api.permissions import ApiKeyHeaderPermission, ApiKeyHeade
from openedx.core.lib.exceptions import CourseNotFoundError from openedx.core.lib.exceptions import CourseNotFoundError
from openedx.core.lib.log_utils import audit_log from openedx.core.lib.log_utils import audit_log
from openedx.features.enterprise_support.api import ( from openedx.features.enterprise_support.api import (
ConsentApiServiceClient, ConsentApiClient,
EnterpriseApiServiceClient, EnterpriseApiClient,
EnterpriseApiException, EnterpriseApiException,
enterprise_enabled enterprise_enabled
) )
...@@ -598,8 +598,8 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn): ...@@ -598,8 +598,8 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
enterprise_course_consent = request.data.get('enterprise_course_consent') enterprise_course_consent = request.data.get('enterprise_course_consent')
explicit_linked_enterprise = request.data.get('linked_enterprise_customer') explicit_linked_enterprise = request.data.get('linked_enterprise_customer')
if (enterprise_course_consent or explicit_linked_enterprise) and has_api_key_permissions and enterprise_enabled(): if (enterprise_course_consent or explicit_linked_enterprise) and has_api_key_permissions and enterprise_enabled():
enterprise_api_client = EnterpriseApiServiceClient() enterprise_api_client = EnterpriseApiClient()
consent_client = ConsentApiServiceClient() consent_client = ConsentApiClient()
# We received an explicitly-linked EnterpriseCustomer for the enrollment # We received an explicitly-linked EnterpriseCustomer for the enrollment
if explicit_linked_enterprise is not None: if explicit_linked_enterprise is not None:
try: try:
......
""" """
APIs providing support for enterprise functionality. APIs providing support for enterprise functionality.
""" """
import hashlib
import logging import logging
from functools import wraps from functools import wraps
import six
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.cache import cache
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.shortcuts import redirect from django.shortcuts import redirect
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.http import urlencode from django.utils.http import urlencode
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from edx_rest_api_client.client import EdxRestApiClient from edx_rest_api_client.client import EdxRestApiClient
from requests.exceptions import ConnectionError, Timeout
from slumber.exceptions import HttpClientError, HttpNotFoundError, HttpServerError, SlumberBaseException from slumber.exceptions import HttpClientError, HttpNotFoundError, HttpServerError, SlumberBaseException
from openedx.core.djangoapps.catalog.models import CatalogIntegration
from openedx.core.djangoapps.catalog.utils import create_catalog_api_client
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.lib.token_utils import JwtBuilder from openedx.core.lib.token_utils import JwtBuilder
from third_party_auth.pipeline import get as get_partial_pipeline from third_party_auth.pipeline import get as get_partial_pipeline
...@@ -24,7 +30,6 @@ try: ...@@ -24,7 +30,6 @@ try:
except ImportError: except ImportError:
pass pass
CONSENT_FAILED_PARAMETER = 'consent_failed' CONSENT_FAILED_PARAMETER = 'consent_failed'
LOGGER = logging.getLogger("edx.enterprise_helpers") LOGGER = logging.getLogger("edx.enterprise_helpers")
...@@ -41,12 +46,12 @@ class ConsentApiClient(object): ...@@ -41,12 +46,12 @@ class ConsentApiClient(object):
Class for producing an Enterprise Consent service API client Class for producing an Enterprise Consent service API client
""" """
def __init__(self, user): def __init__(self):
""" """
Initialize an authenticated Consent service API client by using the Initialize a consent service API client, authenticated using the Enterprise worker username.
provided user.
""" """
jwt = JwtBuilder(user).build_token([]) self.user = User.objects.get(username=settings.ENTERPRISE_SERVICE_WORKER_USERNAME)
jwt = JwtBuilder(self.user).build_token([])
url = configuration_helpers.get_value('ENTERPRISE_CONSENT_API_URL', settings.ENTERPRISE_CONSENT_API_URL) url = configuration_helpers.get_value('ENTERPRISE_CONSENT_API_URL', settings.ENTERPRISE_CONSENT_API_URL)
self.client = EdxRestApiClient( self.client = EdxRestApiClient(
url, url,
...@@ -92,38 +97,17 @@ class ConsentApiClient(object): ...@@ -92,38 +97,17 @@ class ConsentApiClient(object):
return response['consent_required'] return response['consent_required']
class EnterpriseServiceClientMixin(object):
"""
Class for initializing an Enterprise API clients with service user.
"""
def __init__(self):
"""
Initialize an authenticated Enterprise API client by using the
Enterprise worker user by default.
"""
user = User.objects.get(username=settings.ENTERPRISE_SERVICE_WORKER_USERNAME)
super(EnterpriseServiceClientMixin, self).__init__(user)
class ConsentApiServiceClient(EnterpriseServiceClientMixin, ConsentApiClient):
"""
Class for producing an Enterprise Consent API client with service user.
"""
pass
class EnterpriseApiClient(object): class EnterpriseApiClient(object):
""" """
Class for producing an Enterprise service API client. Class for producing an Enterprise service API client.
""" """
def __init__(self, user): def __init__(self):
""" """
Initialize an authenticated Enterprise service API client by using the Initialize an Enterprise service API client, authenticated using the Enterprise worker username.
provided user.
""" """
jwt = JwtBuilder(user).build_token([]) self.user = User.objects.get(username=settings.ENTERPRISE_SERVICE_WORKER_USERNAME)
jwt = JwtBuilder(self.user).build_token([])
self.client = EdxRestApiClient( self.client = EdxRestApiClient(
configuration_helpers.get_value('ENTERPRISE_API_URL', settings.ENTERPRISE_API_URL), configuration_helpers.get_value('ENTERPRISE_API_URL', settings.ENTERPRISE_API_URL),
jwt=jwt jwt=jwt
...@@ -257,13 +241,6 @@ class EnterpriseApiClient(object): ...@@ -257,13 +241,6 @@ class EnterpriseApiClient(object):
return response return response
class EnterpriseApiServiceClient(EnterpriseServiceClientMixin, EnterpriseApiClient):
"""
Class for producing an Enterprise service API client with service user.
"""
pass
def data_sharing_consent_required(view_func): def data_sharing_consent_required(view_func):
""" """
Decorator which makes a view method redirect to the Data Sharing Consent form if: Decorator which makes a view method redirect to the Data Sharing Consent form if:
...@@ -317,7 +294,7 @@ def enterprise_customer_for_request(request): ...@@ -317,7 +294,7 @@ def enterprise_customer_for_request(request):
if not enterprise_enabled(): if not enterprise_enabled():
return None return None
enterprise_customer = None ec = None
sso_provider_id = request.GET.get('tpa_hint') sso_provider_id = request.GET.get('tpa_hint')
running_pipeline = get_partial_pipeline(request) running_pipeline = get_partial_pipeline(request)
...@@ -334,38 +311,34 @@ def enterprise_customer_for_request(request): ...@@ -334,38 +311,34 @@ def enterprise_customer_for_request(request):
# Check if there's an Enterprise Customer such that the linked SSO provider # Check if there's an Enterprise Customer such that the linked SSO provider
# has an ID equal to the ID we got from the running pipeline or from the # has an ID equal to the ID we got from the running pipeline or from the
# request tpa_hint URL parameter. # request tpa_hint URL parameter.
enterprise_customer_uuid = EnterpriseCustomer.objects.get( ec_uuid = EnterpriseCustomer.objects.get(
enterprise_customer_identity_provider__provider_id=sso_provider_id enterprise_customer_identity_provider__provider_id=sso_provider_id
).uuid ).uuid
except EnterpriseCustomer.DoesNotExist: except EnterpriseCustomer.DoesNotExist:
# If there is not an EnterpriseCustomer linked to this SSO provider, set # If there is not an EnterpriseCustomer linked to this SSO provider, set
# the UUID variable to be null. # the UUID variable to be null.
enterprise_customer_uuid = None ec_uuid = None
else: else:
# Check if we got an Enterprise UUID passed directly as either a query # Check if we got an Enterprise UUID passed directly as either a query
# parameter, or as a value in the Enterprise cookie. # parameter, or as a value in the Enterprise cookie.
enterprise_customer_uuid = request.GET.get('enterprise_customer') or request.COOKIES.get( ec_uuid = request.GET.get('enterprise_customer') or request.COOKIES.get(settings.ENTERPRISE_CUSTOMER_COOKIE_NAME)
settings.ENTERPRISE_CUSTOMER_COOKIE_NAME
)
if not enterprise_customer_uuid and request.user.is_authenticated(): if not ec_uuid and request.user.is_authenticated():
# If there's no way to get an Enterprise UUID for the request, check to see # If there's no way to get an Enterprise UUID for the request, check to see
# if there's already an Enterprise attached to the requesting user on the backend. # if there's already an Enterprise attached to the requesting user on the backend.
learner_data = get_enterprise_learner_data(request.site, request.user) learner_data = get_enterprise_learner_data(request.site, request.user)
if learner_data: if learner_data:
enterprise_customer_uuid = learner_data[0]['enterprise_customer']['uuid'] ec_uuid = learner_data[0]['enterprise_customer']['uuid']
if enterprise_customer_uuid: if ec_uuid:
# If we were able to obtain an EnterpriseCustomer UUID, go ahead # If we were able to obtain an EnterpriseCustomer UUID, go ahead
# and use it to attempt to retrieve EnterpriseCustomer details # and use it to attempt to retrieve EnterpriseCustomer details
# from the EnterpriseCustomer API. # from the EnterpriseCustomer API.
try: try:
enterprise_customer = EnterpriseApiClient(user=request.user).get_enterprise_customer( ec = EnterpriseApiClient().get_enterprise_customer(ec_uuid)
enterprise_customer_uuid
)
except HttpNotFoundError: except HttpNotFoundError:
enterprise_customer = None ec = None
return enterprise_customer return ec
def consent_needed_for_course(request, user, course_id, enrollment_exists=False): def consent_needed_for_course(request, user, course_id, enrollment_exists=False):
...@@ -385,7 +358,7 @@ def consent_needed_for_course(request, user, course_id, enrollment_exists=False) ...@@ -385,7 +358,7 @@ def consent_needed_for_course(request, user, course_id, enrollment_exists=False)
if not enterprise_learner_details: if not enterprise_learner_details:
consent_needed = False consent_needed = False
else: else:
client = ConsentApiClient(user=request.user) client = ConsentApiClient()
consent_needed = any( consent_needed = any(
client.consent_required( client.consent_required(
username=user.username, username=user.username,
...@@ -453,7 +426,7 @@ def get_enterprise_learner_data(site, user): ...@@ -453,7 +426,7 @@ def get_enterprise_learner_data(site, user):
if not enterprise_enabled(): if not enterprise_enabled():
return None return None
enterprise_learner_data = EnterpriseApiClient(user=user).fetch_enterprise_learner_data(site=site, user=user) enterprise_learner_data = EnterpriseApiClient().fetch_enterprise_learner_data(site=site, user=user)
if enterprise_learner_data: if enterprise_learner_data:
return enterprise_learner_data['results'] return enterprise_learner_data['results']
...@@ -488,7 +461,7 @@ def get_dashboard_consent_notification(request, user, course_enrollments): ...@@ -488,7 +461,7 @@ def get_dashboard_consent_notification(request, user, course_enrollments):
enrollment = course_enrollment enrollment = course_enrollment
break break
client = ConsentApiClient(user=request.user) client = ConsentApiClient()
consent_needed = client.consent_required( consent_needed = client.consent_required(
enterprise_customer_uuid=enterprise_customer['uuid'], enterprise_customer_uuid=enterprise_customer['uuid'],
username=user.username, username=user.username,
......
...@@ -8,20 +8,16 @@ import ddt ...@@ -8,20 +8,16 @@ import ddt
import httpretty import httpretty
import mock import mock
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from openedx.features.enterprise_support.api import ( from openedx.features.enterprise_support.api import (
ConsentApiClient,
ConsentApiServiceClient,
consent_needed_for_course, consent_needed_for_course,
data_sharing_consent_required, data_sharing_consent_required,
EnterpriseApiClient,
EnterpriseApiServiceClient,
enterprise_customer_for_request, enterprise_customer_for_request,
enterprise_enabled,
get_dashboard_consent_notification, get_dashboard_consent_notification,
get_enterprise_consent_url, get_enterprise_consent_url,
) )
...@@ -48,86 +44,21 @@ class TestEnterpriseApi(EnterpriseServiceMockMixin, TestCase): ...@@ -48,86 +44,21 @@ class TestEnterpriseApi(EnterpriseServiceMockMixin, TestCase):
""" """
@classmethod @classmethod
def setUpTestData(cls): def setUpTestData(cls):
cls.user = UserFactory.create( UserFactory.create(
username=settings.ENTERPRISE_SERVICE_WORKER_USERNAME, username='enterprise_worker',
email='ent_worker@example.com', email='ent_worker@example.com',
password='password123', password='password123',
) )
super(TestEnterpriseApi, cls).setUpTestData() super(TestEnterpriseApi, cls).setUpTestData()
def _assert_api_service_client(self, api_client, mocked_jwt_builder):
"""
Verify that the provided api client uses the enterprise service user to generate
JWT token for auth.
"""
mocked_jwt_builder.return_value.build_token.return_value = 'test-token'
enterprise_service_user = User.objects.get(username=settings.ENTERPRISE_SERVICE_WORKER_USERNAME)
enterprise_api_service_client = api_client()
mocked_jwt_builder.assert_called_once_with(enterprise_service_user)
# pylint: disable=protected-access
self.assertEqual(enterprise_api_service_client.client._store['session'].auth.token, 'test-token')
def _assert_api_client_with_user(self, api_client, mocked_jwt_builder):
"""
Verify that the provided api client uses the expected user to generate
JWT token for auth.
"""
mocked_jwt_builder.return_value.build_token.return_value = 'test-token'
dummy_enterprise_user = UserFactory.create(
username='dummy-enterprise-user',
email='dummy-enterprise-user@example.com',
password='password123',
)
enterprise_api_service_client = api_client(dummy_enterprise_user)
mocked_jwt_builder.assert_called_once_with(dummy_enterprise_user)
# pylint: disable=protected-access
self.assertEqual(enterprise_api_service_client.client._store['session'].auth.token, 'test-token')
@httpretty.activate
@mock.patch('openedx.features.enterprise_support.api.JwtBuilder')
def test_enterprise_api_client_with_service_user(self, mock_jwt_builder):
"""
Verify that enterprise API service client uses enterprise service user
by default to authenticate and access enterprise API.
"""
self._assert_api_service_client(EnterpriseApiServiceClient, mock_jwt_builder)
@httpretty.activate
@mock.patch('openedx.features.enterprise_support.api.JwtBuilder')
def test_enterprise_api_client_with_user(self, mock_jwt_builder):
"""
Verify that enterprise API client uses the provided user to
authenticate and access enterprise API.
"""
self._assert_api_client_with_user(EnterpriseApiClient, mock_jwt_builder)
@httpretty.activate
@mock.patch('openedx.features.enterprise_support.api.JwtBuilder')
def test_enterprise_consent_api_client_with_service_user(self, mock_jwt_builder):
"""
Verify that enterprise API consent service client uses enterprise
service user by default to authenticate and access enterprise API.
"""
self._assert_api_service_client(ConsentApiServiceClient, mock_jwt_builder)
@httpretty.activate
@mock.patch('openedx.features.enterprise_support.api.JwtBuilder')
def test_enterprise_consent_api_client_with_user(self, mock_jwt_builder):
"""
Verify that enterprise API consent service client uses the provided
user to authenticate and access enterprise API.
"""
self._assert_api_client_with_user(ConsentApiClient, mock_jwt_builder)
@httpretty.activate @httpretty.activate
@override_settings(ENTERPRISE_SERVICE_WORKER_USERNAME='enterprise_worker')
def test_consent_needed_for_course(self): def test_consent_needed_for_course(self):
user = mock.MagicMock( user = mock.MagicMock(
username='janedoe', username='janedoe',
is_authenticated=lambda: True, is_authenticated=lambda: True,
) )
request = mock.MagicMock(session={}, user=user) request = mock.MagicMock(session={})
self.mock_enterprise_learner_api() self.mock_enterprise_learner_api()
self.mock_consent_missing(user.username, 'fake-course', 'cf246b88-d5f6-4908-a522-fc307e0b0c59') self.mock_consent_missing(user.username, 'fake-course', 'cf246b88-d5f6-4908-a522-fc307e0b0c59')
self.assertTrue(consent_needed_for_course(request, user, 'fake-course')) self.assertTrue(consent_needed_for_course(request, user, 'fake-course'))
...@@ -143,65 +74,63 @@ class TestEnterpriseApi(EnterpriseServiceMockMixin, TestCase): ...@@ -143,65 +74,63 @@ class TestEnterpriseApi(EnterpriseServiceMockMixin, TestCase):
@mock.patch('openedx.features.enterprise_support.api.EnterpriseCustomer') @mock.patch('openedx.features.enterprise_support.api.EnterpriseCustomer')
@mock.patch('openedx.features.enterprise_support.api.get_partial_pipeline') @mock.patch('openedx.features.enterprise_support.api.get_partial_pipeline')
@mock.patch('openedx.features.enterprise_support.api.Registry') @mock.patch('openedx.features.enterprise_support.api.Registry')
@override_settings(ENTERPRISE_SERVICE_WORKER_USERNAME='enterprise_worker')
def test_enterprise_customer_for_request( def test_enterprise_customer_for_request(
self, self,
mock_registry, mock_registry,
mock_partial, mock_partial,
mock_enterprise_customer_model, mock_ec_model,
mock_get_el_data mock_get_el_data
): ):
def mock_get_enterprise_customer(**kwargs): def mock_get_ec(**kwargs):
uuid = kwargs.get('enterprise_customer_identity_provider__provider_id') uuid = kwargs.get('enterprise_customer_identity_provider__provider_id')
if uuid: if uuid:
return mock.MagicMock(uuid=uuid, user=self.user) return mock.MagicMock(uuid=uuid)
raise Exception raise Exception
dummy_request = mock.MagicMock(session={}, user=self.user) mock_ec_model.objects.get.side_effect = mock_get_ec
mock_enterprise_customer_model.objects.get.side_effect = mock_get_enterprise_customer mock_ec_model.DoesNotExist = Exception
mock_enterprise_customer_model.DoesNotExist = Exception
mock_partial.return_value = True mock_partial.return_value = True
mock_registry.get_from_pipeline.return_value.provider_id = 'real-ent-uuid' mock_registry.get_from_pipeline.return_value.provider_id = 'real-ent-uuid'
self.mock_get_enterprise_customer('real-ent-uuid', {'real': 'enterprisecustomer'}, 200) self.mock_get_enterprise_customer('real-ent-uuid', {"real": "enterprisecustomer"}, 200)
enterprise_customer = enterprise_customer_for_request(dummy_request) ec = enterprise_customer_for_request(mock.MagicMock())
self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'}) self.assertEqual(ec, {"real": "enterprisecustomer"})
httpretty.reset() httpretty.reset()
self.mock_get_enterprise_customer('real-ent-uuid', {'detail': 'Not found.'}, 404) self.mock_get_enterprise_customer('real-ent-uuid', {"detail": "Not found."}, 404)
enterprise_customer = enterprise_customer_for_request(dummy_request) ec = enterprise_customer_for_request(mock.MagicMock())
self.assertIsNone(enterprise_customer) self.assertIsNone(ec)
mock_registry.get_from_pipeline.return_value.provider_id = None mock_registry.get_from_pipeline.return_value.provider_id = None
httpretty.reset() httpretty.reset()
self.mock_get_enterprise_customer('real-ent-uuid', {'real': 'enterprisecustomer'}, 200) self.mock_get_enterprise_customer('real-ent-uuid', {"real": "enterprisecustomer"}, 200)
enterprise_customer = enterprise_customer_for_request( ec = enterprise_customer_for_request(mock.MagicMock(GET={"enterprise_customer": 'real-ent-uuid'}))
mock.MagicMock(GET={'enterprise_customer': 'real-ent-uuid'}, user=self.user)
)
self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'}) self.assertEqual(ec, {"real": "enterprisecustomer"})
enterprise_customer = enterprise_customer_for_request( ec = enterprise_customer_for_request(
mock.MagicMock(GET={}, COOKIES={settings.ENTERPRISE_CUSTOMER_COOKIE_NAME: 'real-ent-uuid'}, user=self.user) mock.MagicMock(GET={}, COOKIES={settings.ENTERPRISE_CUSTOMER_COOKIE_NAME: 'real-ent-uuid'})
) )
self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'}) self.assertEqual(ec, {"real": "enterprisecustomer"})
mock_get_el_data.return_value = [{'enterprise_customer': {'uuid': 'real-ent-uuid'}}] mock_get_el_data.return_value = [{'enterprise_customer': {'uuid': 'real-ent-uuid'}}]
enterprise_customer = enterprise_customer_for_request( ec = enterprise_customer_for_request(
mock.MagicMock(GET={}, COOKIES={}, user=self.user, site=1) mock.MagicMock(GET={}, COOKIES={}, user=mock.MagicMock(is_authenticated=lambda: True), site=1)
) )
self.assertEqual(enterprise_customer, {'real': 'enterprisecustomer'}) self.assertEqual(ec, {"real": "enterprisecustomer"})
def check_data_sharing_consent(self, consent_required=False, consent_url=None): def check_data_sharing_consent(self, consent_required=False, consent_url=None):
""" """
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment