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
from openedx.core.lib.exceptions import CourseNotFoundError
from openedx.core.lib.log_utils import audit_log
from openedx.features.enterprise_support.api import (
ConsentApiClient,
EnterpriseApiClient,
ConsentApiServiceClient,
EnterpriseApiServiceClient,
EnterpriseApiException,
enterprise_enabled
)
......@@ -598,8 +598,8 @@ class EnrollmentListView(APIView, ApiKeyPermissionMixIn):
enterprise_course_consent = request.data.get('enterprise_course_consent')
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():
enterprise_api_client = EnterpriseApiClient()
consent_client = ConsentApiClient()
enterprise_api_client = EnterpriseApiServiceClient()
consent_client = ConsentApiServiceClient()
# We received an explicitly-linked EnterpriseCustomer for the enrollment
if explicit_linked_enterprise is not None:
try:
......
"""
APIs providing support for enterprise functionality.
"""
import hashlib
import logging
from functools import wraps
import six
from django.conf import settings
from django.contrib.auth.models import User
from django.core.cache import cache
......@@ -15,13 +13,11 @@ from django.template.loader import render_to_string
from django.utils.http import urlencode
from django.utils.translation import ugettext as _
from edx_rest_api_client.client import EdxRestApiClient
from requests.exceptions import ConnectionError, Timeout
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.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.provider import Registry
......@@ -30,6 +26,7 @@ try:
except ImportError:
pass
CONSENT_FAILED_PARAMETER = 'consent_failed'
LOGGER = logging.getLogger("edx.enterprise_helpers")
......@@ -46,12 +43,12 @@ class ConsentApiClient(object):
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(self.user).build_token([])
jwt = JwtBuilder(user).build_token([])
url = configuration_helpers.get_value('ENTERPRISE_CONSENT_API_URL', settings.ENTERPRISE_CONSENT_API_URL)
self.client = EdxRestApiClient(
url,
......@@ -97,17 +94,38 @@ class ConsentApiClient(object):
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 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(self.user).build_token([])
jwt = JwtBuilder(user).build_token([])
self.client = EdxRestApiClient(
configuration_helpers.get_value('ENTERPRISE_API_URL', settings.ENTERPRISE_API_URL),
jwt=jwt
......@@ -238,6 +256,30 @@ class EnterpriseApiClient(object):
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):
"""
Decorator which makes a view method redirect to the Data Sharing Consent form if:
......@@ -291,7 +333,7 @@ def enterprise_customer_for_request(request):
if not enterprise_enabled():
return None
ec = None
enterprise_customer = None
sso_provider_id = request.GET.get('tpa_hint')
running_pipeline = get_partial_pipeline(request)
......@@ -308,34 +350,34 @@ def enterprise_customer_for_request(request):
# 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
# 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
).uuid
except EnterpriseCustomer.DoesNotExist:
# If there is not an EnterpriseCustomer linked to this SSO provider, set
# the UUID variable to be null.
ec_uuid = None
enterprise_customer_uuid = None
else:
# Check if we got an Enterprise UUID passed directly as either a query
# 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)
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.
learner_data = get_enterprise_learner_data(request.site, request.user)
if learner_data:
ec_uuid = learner_data[0]['enterprise_customer']['uuid']
if ec_uuid:
enterprise_customer_uuid = request.GET.get('enterprise_customer') or request.COOKIES.get(
settings.ENTERPRISE_CUSTOMER_COOKIE_NAME
)
if enterprise_customer_uuid:
# If we were able to obtain an EnterpriseCustomer UUID, go ahead
# and use it to attempt to retrieve EnterpriseCustomer details
# from the EnterpriseCustomer API.
enterprise_api_client = EnterpriseApiServiceClient()
if request.user.is_authenticated():
enterprise_api_client = EnterpriseApiClient(user=request.user)
try:
ec = EnterpriseApiClient().get_enterprise_customer(ec_uuid)
enterprise_customer = enterprise_api_client.get_enterprise_customer(enterprise_customer_uuid)
except HttpNotFoundError:
ec = None
enterprise_customer = None
return ec
return enterprise_customer
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:
consent_needed = False
else:
client = ConsentApiClient()
client = ConsentApiClient(user=request.user)
consent_needed = any(
client.consent_required(
username=user.username,
......@@ -423,7 +465,7 @@ def get_enterprise_learner_data(site, user):
if not enterprise_enabled():
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:
return enterprise_learner_data['results']
......@@ -458,7 +500,7 @@ def get_dashboard_consent_notification(request, user, course_enrollments):
enrollment = course_enrollment
break
client = ConsentApiClient()
client = ConsentApiClient(user=request.user)
consent_needed = client.consent_required(
enterprise_customer_uuid=enterprise_customer['uuid'],
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