Commit 24df3140 by Zubair Afzal Committed by GitHub

Merge pull request #16067 from edx/zub/ENT-624-update-enterprise-api-support

Ent 624 update enterprise api support
parents 643d7d21 4040c45f
...@@ -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 (
ConsentApiClient, ConsentApiServiceClient,
EnterpriseApiClient, EnterpriseApiServiceClient,
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 = EnterpriseApiClient() enterprise_api_client = EnterpriseApiServiceClient()
consent_client = ConsentApiClient() consent_client = ConsentApiServiceClient()
# 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.cache import cache
...@@ -15,13 +13,11 @@ from django.template.loader import render_to_string ...@@ -15,13 +13,11 @@ 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 openedx.features.enterprise_support.utils import get_cache_key
from third_party_auth.pipeline import get as get_partial_pipeline from third_party_auth.pipeline import get as get_partial_pipeline
from third_party_auth.provider import Registry from third_party_auth.provider import Registry
...@@ -30,6 +26,7 @@ try: ...@@ -30,6 +26,7 @@ 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")
...@@ -46,12 +43,12 @@ class ConsentApiClient(object): ...@@ -46,12 +43,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): def __init__(self, user):
""" """
Initialize a consent service API client, authenticated using the Enterprise worker username. Initialize an authenticated Consent service API client by using the
provided user.
""" """
self.user = User.objects.get(username=settings.ENTERPRISE_SERVICE_WORKER_USERNAME) jwt = JwtBuilder(user).build_token([])
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,
...@@ -97,17 +94,38 @@ class ConsentApiClient(object): ...@@ -97,17 +94,38 @@ 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): def __init__(self, user):
""" """
Initialize an Enterprise service API client, authenticated using the Enterprise worker username. Initialize an authenticated Enterprise service API client by using the
provided user.
""" """
self.user = User.objects.get(username=settings.ENTERPRISE_SERVICE_WORKER_USERNAME) jwt = JwtBuilder(user).build_token([])
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
...@@ -238,6 +256,30 @@ class EnterpriseApiClient(object): ...@@ -238,6 +256,30 @@ class EnterpriseApiClient(object):
return response return response
class EnterpriseApiServiceClient(EnterpriseServiceClientMixin, EnterpriseApiClient):
"""
Class for producing an Enterprise service API client with service user.
"""
def get_enterprise_customer(self, uuid):
"""
Fetch enterprise customer with enterprise service user and cache the
API response`.
"""
cache_key = get_cache_key(
resource='enterprise-customer',
username=settings.ENTERPRISE_SERVICE_WORKER_USERNAME,
)
enterprise_customer = cache.get(cache_key)
if not enterprise_customer:
endpoint = getattr(self.client, 'enterprise-customer')
enterprise_customer = endpoint(uuid).get()
if enterprise_customer:
cache.set(cache_key, enterprise_customer, settings.ENTERPRISE_API_CACHE_TIMEOUT)
return enterprise_customer
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:
...@@ -291,7 +333,7 @@ def enterprise_customer_for_request(request): ...@@ -291,7 +333,7 @@ def enterprise_customer_for_request(request):
if not enterprise_enabled(): if not enterprise_enabled():
return None return None
ec = None enterprise_customer = 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)
...@@ -308,34 +350,34 @@ def enterprise_customer_for_request(request): ...@@ -308,34 +350,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.
ec_uuid = EnterpriseCustomer.objects.get( enterprise_customer_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.
ec_uuid = None enterprise_customer_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.
ec_uuid = request.GET.get('enterprise_customer') or request.COOKIES.get(settings.ENTERPRISE_CUSTOMER_COOKIE_NAME) enterprise_customer_uuid = request.GET.get('enterprise_customer') or request.COOKIES.get(
settings.ENTERPRISE_CUSTOMER_COOKIE_NAME
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 already an Enterprise attached to the requesting user on the backend. if enterprise_customer_uuid:
learner_data = get_enterprise_learner_data(request.site, request.user)
if learner_data:
ec_uuid = learner_data[0]['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.
enterprise_api_client = EnterpriseApiServiceClient()
if request.user.is_authenticated():
enterprise_api_client = EnterpriseApiClient(user=request.user)
try: try:
ec = EnterpriseApiClient().get_enterprise_customer(ec_uuid) enterprise_customer = enterprise_api_client.get_enterprise_customer(enterprise_customer_uuid)
except HttpNotFoundError: except HttpNotFoundError:
ec = None enterprise_customer = None
return ec return enterprise_customer
def consent_needed_for_course(request, user, course_id, enrollment_exists=False): def consent_needed_for_course(request, user, course_id, enrollment_exists=False):
...@@ -355,7 +397,7 @@ def consent_needed_for_course(request, user, course_id, enrollment_exists=False) ...@@ -355,7 +397,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() client = ConsentApiClient(user=request.user)
consent_needed = any( consent_needed = any(
client.consent_required( client.consent_required(
username=user.username, username=user.username,
...@@ -423,7 +465,7 @@ def get_enterprise_learner_data(site, user): ...@@ -423,7 +465,7 @@ def get_enterprise_learner_data(site, user):
if not enterprise_enabled(): if not enterprise_enabled():
return None return None
enterprise_learner_data = EnterpriseApiClient().fetch_enterprise_learner_data(site=site, user=user) enterprise_learner_data = EnterpriseApiClient(user=user).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']
...@@ -458,7 +500,7 @@ def get_dashboard_consent_notification(request, user, course_enrollments): ...@@ -458,7 +500,7 @@ def get_dashboard_consent_notification(request, user, course_enrollments):
enrollment = course_enrollment enrollment = course_enrollment
break break
client = ConsentApiClient() client = ConsentApiClient(user=request.user)
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,
......
from __future__ import unicode_literals
import hashlib
import six
def get_cache_key(**kwargs):
"""
Get MD5 encoded cache key for given arguments.
Here is the format of key before MD5 encryption.
key1:value1__key2:value2 ...
Example:
>>> get_cache_key(site_domain="example.com", resource="enterprise-learner")
# Here is key format for above call
# "site_domain:example.com__resource:enterprise-learner"
a54349175618ff1659dee0978e3149ca
Arguments:
**kwargs: Key word arguments that need to be present in cache key.
Returns:
An MD5 encoded key uniquely identified by the key word arguments.
"""
key = '__'.join(['{}:{}'.format(item, value) for item, value in six.iteritems(kwargs)])
return hashlib.md5(key).hexdigest()
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