diff --git a/common/djangoapps/enrollment/tests/test_views.py b/common/djangoapps/enrollment/tests/test_views.py
index a74dea421faee4dfedfef45d35f8a073b0f8cfc5..580cbd8002221abf2fffcee1c1b3978c01a3126f 100644
--- a/common/djangoapps/enrollment/tests/test_views.py
+++ b/common/djangoapps/enrollment/tests/test_views.py
@@ -33,6 +33,7 @@ from openedx.core.djangoapps.embargo.test_utils import restrict_course
 from openedx.core.djangoapps.user_api.models import UserOrgTag
 from openedx.core.lib.django_test_client_utils import get_absolute_url
 from openedx.core.lib.token_utils import JwtBuilder
+from openedx.features.enterprise_support.tests import FAKE_ENTERPRISE_CUSTOMER
 from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseServiceMockMixin
 from student.models import CourseEnrollment
 from student.roles import CourseStaffRole
@@ -932,7 +933,8 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente
     @httpretty.activate
     @override_settings(ENTERPRISE_SERVICE_WORKER_USERNAME='enterprise_worker',
                        FEATURES=dict(ENABLE_ENTERPRISE_INTEGRATION=True))
-    def test_enterprise_course_enrollment_with_ec_uuid(self):
+    @patch('openedx.features.enterprise_support.api.enterprise_customer_from_api')
+    def test_enterprise_course_enrollment_with_ec_uuid(self, mock_enterprise_customer_from_api):
         """Verify that the enrollment completes when the EnterpriseCourseEnrollment creation succeeds. """
         UserFactory.create(
             username='enterprise_worker',
@@ -949,6 +951,7 @@ class EnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase, Ente
             'course_id': unicode(self.course.id),
             'ec_uuid': 'this-is-a-real-uuid'
         }
+        mock_enterprise_customer_from_api.return_value = FAKE_ENTERPRISE_CUSTOMER
         self.mock_enterprise_course_enrollment_post_api()
         self.mock_consent_missing(**consent_kwargs)
         self.mock_consent_post(**consent_kwargs)
diff --git a/common/djangoapps/util/views.py b/common/djangoapps/util/views.py
index cdf5f860a355e31adc5d0ff285c97e6fe4cfa559..52a262eb33e5485033cba8ef1fc9a5b60ac4ea40 100644
--- a/common/djangoapps/util/views.py
+++ b/common/djangoapps/util/views.py
@@ -454,8 +454,8 @@ def submit_feedback(request):
     success = False
     context = get_feedback_form_context(request)
 
-    #Update the tag info with 'enterprise_learner' if the user belongs to an enterprise customer.
-    enterprise_learner_data = enterprise_api.get_enterprise_learner_data(site=request.site, user=request.user)
+    # Update the tag info with 'enterprise_learner' if the user belongs to an enterprise customer.
+    enterprise_learner_data = enterprise_api.get_enterprise_learner_data(user=request.user)
     if enterprise_learner_data:
         context["tags"]["learner_type"] = "enterprise_learner"
 
diff --git a/lms/djangoapps/support/views/contact_us.py b/lms/djangoapps/support/views/contact_us.py
index b8753e3448ec1640e71329870078ab2a88dcccb3..ff74e10c008e370e6f958150c9c0fb6f7ed54fef 100644
--- a/lms/djangoapps/support/views/contact_us.py
+++ b/lms/djangoapps/support/views/contact_us.py
@@ -33,7 +33,7 @@ class ContactUsView(View):
 
         if request.user.is_authenticated():
             context['user_enrollments'] = CourseEnrollment.enrollments_for_user_with_overviews_preload(request.user)
-            enterprise_learner_data = enterprise_api.get_enterprise_learner_data(site=request.site, user=request.user)
+            enterprise_learner_data = enterprise_api.get_enterprise_learner_data(user=request.user)
             if enterprise_learner_data:
                 tags.append('enterprise_learner')
 
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 8a1b384f85ff066fa6737348f0b5123b08c8233e..8cb39b82e9e1b58459f75f5d709aaf2270bbefbc 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -1275,8 +1275,6 @@ MIDDLEWARE_CLASSES = [
     # Must be after DarkLangMiddleware.
     'django.middleware.locale.LocaleMiddleware',
 
-    # 'debug_toolbar.middleware.DebugToolbarMiddleware',
-
     'django_comment_client.utils.ViewNameMiddleware',
     'codejail.django_integration.ConfigureCodeJailMiddleware',
 
@@ -1298,6 +1296,9 @@ MIDDLEWARE_CLASSES = [
 
     'waffle.middleware.WaffleMiddleware',
 
+    # Inserts Enterprise content.
+    'openedx.features.enterprise_support.middleware.EnterpriseMiddleware',
+
     # This must be last
     'openedx.core.djangoapps.site_configuration.middleware.SessionCookieDomainOverrideMiddleware',
 ]
diff --git a/lms/urls.py b/lms/urls.py
index 3cb63c264ccee2474a8231c48a59d0d3ff2525bb..ff6a8be4756e1cccf157a6cf7bf5b3a65bfc8427 100644
--- a/lms/urls.py
+++ b/lms/urls.py
@@ -143,7 +143,7 @@ urlpatterns = [
 
 # TODO: This needs to move to a separate urls.py once the student_account and
 # student views below find a home together
-if settings.FEATURES['ENABLE_COMBINED_LOGIN_REGISTRATION']:
+if settings.FEATURES.get('ENABLE_COMBINED_LOGIN_REGISTRATION'):
     # Backwards compatibility with old URL structure, but serve the new views
     urlpatterns += [
         url(r'^login$', student_account_views.login_and_registration_form,
@@ -158,12 +158,12 @@ else:
         url(r'^register$', student_views.register_user, name='register_user'),
     ]
 
-if settings.FEATURES['ENABLE_MOBILE_REST_API']:
+if settings.FEATURES.get('ENABLE_MOBILE_REST_API'):
     urlpatterns += [
         url(r'^api/mobile/v0.5/', include('mobile_api.urls')),
     ]
 
-if settings.FEATURES['ENABLE_OPENBADGES']:
+if settings.FEATURES.get('ENABLE_OPENBADGES'):
     urlpatterns += [
         url(r'^api/badges/v1/', include('badges.api.urls', app_name='badges', namespace='badges_api')),
     ]
@@ -174,7 +174,7 @@ urlpatterns += [
 
 
 # sysadmin dashboard, to see what courses are loaded, to delete & load courses
-if settings.FEATURES['ENABLE_SYSADMIN_DASHBOARD']:
+if settings.FEATURES.get('ENABLE_SYSADMIN_DASHBOARD'):
     urlpatterns += [
         url(r'^sysadmin/', include('dashboard.sysadmin_urls')),
     ]
@@ -675,7 +675,7 @@ urlpatterns += [
     ),
 ]
 
-if settings.FEATURES['ENABLE_TEAMS']:
+if settings.FEATURES.get('ENABLE_TEAMS'):
     # Teams endpoints
     urlpatterns += [
         url(
@@ -831,7 +831,7 @@ if settings.FEATURES.get('RESTRICT_ENROLL_BY_REG_METHOD'):
             external_auth_views.course_specific_register, name='course-specific-register'),
     ]
 
-if configuration_helpers.get_value('ENABLE_BULK_ENROLLMENT_VIEW', settings.FEATURES['ENABLE_BULK_ENROLLMENT_VIEW']):
+if configuration_helpers.get_value('ENABLE_BULK_ENROLLMENT_VIEW', settings.FEATURES.get('ENABLE_BULK_ENROLLMENT_VIEW')):
     urlpatterns += [
         url(r'^api/bulk_enroll/v1/', include('bulk_enroll.urls')),
     ]
@@ -994,7 +994,7 @@ urlpatterns += [
 ]
 
 # Custom courses on edX (CCX) URLs
-if settings.FEATURES['CUSTOM_COURSES_EDX']:
+if settings.FEATURES.get('CUSTOM_COURSES_EDX'):
     urlpatterns += [
         url(r'^courses/{}/'.format(settings.COURSE_ID_PATTERN),
             include('ccx.urls')),
diff --git a/openedx/features/enterprise_support/api.py b/openedx/features/enterprise_support/api.py
index afa044adaf07881a3766387c1ed4cfb67be47e73..d5487a0d5d6b4ee48920454850596685c7caffb2 100644
--- a/openedx/features/enterprise_support/api.py
+++ b/openedx/features/enterprise_support/api.py
@@ -161,12 +161,12 @@ class EnterpriseApiClient(object):
             LOGGER.exception(message)
             raise EnterpriseApiException(message)
 
-    def fetch_enterprise_learner_data(self, site, user):  # pylint: disable=unused-argument
+    def fetch_enterprise_learner_data(self, user):
         """
         Fetch information related to enterprise from the Enterprise Service.
 
         Example:
-            fetch_enterprise_learner_data(site, user)
+            fetch_enterprise_learner_data(user)
 
         Argument:
             site: (Site) site instance
@@ -269,17 +269,12 @@ class EnterpriseApiServiceClient(EnterpriseServiceClientMixin, EnterpriseApiClie
         Fetch enterprise customer with enterprise service user and cache the
         API response`.
         """
-        cache_key = get_cache_key(
-            resource='enterprise-customer',
-            resource_id=uuid,
-            username=settings.ENTERPRISE_SERVICE_WORKER_USERNAME,
-        )
-        enterprise_customer = cache.get(cache_key)
+        enterprise_customer = enterprise_customer_from_cache(uuid=uuid)
         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)
+                cache_enterprise(enterprise_customer)
 
         return enterprise_customer
 
@@ -328,14 +323,79 @@ def enterprise_enabled():
     return 'enterprise' in settings.INSTALLED_APPS and settings.FEATURES.get('ENABLE_ENTERPRISE_INTEGRATION', False)
 
 
+def enterprise_is_enabled(otherwise=None):
+    """Decorator which requires that the Enterprise feature be enabled before the function can run."""
+    def decorator(func):
+        """Decorator for ensuring the Enterprise feature is enabled."""
+        def wrapper(*args, **kwargs):
+            if enterprise_enabled():
+                return func(*args, **kwargs)
+            return otherwise
+        return wrapper
+    return decorator
+
+
+@enterprise_is_enabled()
+def get_enterprise_customer_cache_key(uuid, username=settings.ENTERPRISE_SERVICE_WORKER_USERNAME):
+    """The cache key used to get cached Enterprise Customer data."""
+    return get_cache_key(
+        resource='enterprise-customer',
+        resource_id=uuid,
+        username=username,
+    )
+
+
+@enterprise_is_enabled()
+def cache_enterprise(enterprise_customer):
+    """Cache this customer's data."""
+    cache_key = get_enterprise_customer_cache_key(enterprise_customer['uuid'])
+    cache.set(cache_key, enterprise_customer, settings.ENTERPRISE_API_CACHE_TIMEOUT)
+
+
+@enterprise_is_enabled()
+def enterprise_customer_from_cache(request=None, uuid=None):
+    """Check all available caches for Enterprise Customer data."""
+    enterprise_customer = None
+
+    # Check if it's cached in the general cache storage.
+    if uuid:
+        cache_key = get_enterprise_customer_cache_key(uuid)
+        enterprise_customer = cache.get(cache_key)
+
+    # Check if it's cached in the session.
+    if not enterprise_customer and request and request.user.is_authenticated():
+        enterprise_customer = request.session.get('enterprise_customer')
+
+    return enterprise_customer
+
+
+@enterprise_is_enabled()
+def enterprise_customer_from_api(request):
+    """Use an API to get Enterprise Customer data from request context clues."""
+    enterprise_customer = None
+    enterprise_customer_uuid = enterprise_customer_uuid_for_request(request)
+    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 = (
+            EnterpriseApiClient(user=request.user)
+            if request.user.is_authenticated()
+            else EnterpriseApiServiceClient()
+        )
+
+        try:
+            enterprise_customer = enterprise_api_client.get_enterprise_customer(enterprise_customer_uuid)
+        except HttpNotFoundError:
+            enterprise_customer = None
+    return enterprise_customer
+
+
+@enterprise_is_enabled()
 def enterprise_customer_uuid_for_request(request):
     """
     Check all the context clues of the request to gather a particular EnterpriseCustomer's UUID.
     """
-    if not enterprise_enabled():
-        return None
-
-    enterprise_customer_uuid = None
     sso_provider_id = request.GET.get('tpa_hint')
     running_pipeline = get_partial_pipeline(request)
     if running_pipeline:
@@ -355,7 +415,7 @@ def enterprise_customer_uuid_for_request(request):
                 enterprise_customer_identity_provider__provider_id=sso_provider_id
             ).uuid
         except EnterpriseCustomer.DoesNotExist:
-            pass
+            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.
@@ -366,50 +426,37 @@ def enterprise_customer_uuid_for_request(request):
     if not enterprise_customer_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)
+        learner_data = get_enterprise_learner_data(request.user)
         if learner_data:
             enterprise_customer_uuid = learner_data[0]['enterprise_customer']['uuid']
 
     return enterprise_customer_uuid
 
 
+@enterprise_is_enabled()
 def enterprise_customer_for_request(request):
     """
     Check all the context clues of the request to determine if
     the request being made is tied to a particular EnterpriseCustomer.
     """
-    enterprise_customer = None
-    enterprise_customer_uuid = enterprise_customer_uuid_for_request(request)
-    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:
-            enterprise_customer = enterprise_api_client.get_enterprise_customer(enterprise_customer_uuid)
-        except HttpNotFoundError:
-            enterprise_customer = None
-
-    return enterprise_customer
+    if 'enterprise_customer' in request.session:
+        return enterprise_customer_from_cache(request=request)
+    else:
+        return enterprise_customer_from_api(request)
 
 
+@enterprise_is_enabled(otherwise=False)
 def consent_needed_for_course(request, user, course_id, enrollment_exists=False):
     """
     Wrap the enterprise app check to determine if the user needs to grant
     data sharing permissions before accessing a course.
     """
-    if not enterprise_enabled():
-        return False
-
     consent_key = ('data_sharing_consent_needed', course_id)
 
     if request.session.get(consent_key) is False:
         return False
 
-    enterprise_learner_details = get_enterprise_learner_data(request.site, user)
+    enterprise_learner_details = get_enterprise_learner_data(user)
     if not enterprise_learner_details:
         consent_needed = False
     else:
@@ -431,15 +478,13 @@ def consent_needed_for_course(request, user, course_id, enrollment_exists=False)
     return consent_needed
 
 
+@enterprise_is_enabled(otherwise=set())
 def get_consent_required_courses(user, course_ids):
     """
     Returns a set of course_ids that require consent
     Note that this function makes use of the Enterprise models directly instead of using the API calls
     """
     result = set()
-    if not enterprise_enabled():
-        return result
-
     enterprise_learner = EnterpriseCustomerUser.objects.filter(user_id=user.id).first()
     if not enterprise_learner or not enterprise_learner.enterprise_customer:
         return result
@@ -456,6 +501,7 @@ def get_consent_required_courses(user, course_ids):
     return result
 
 
+@enterprise_is_enabled(otherwise='')
 def get_enterprise_consent_url(request, course_id, user=None, return_to=None, enrollment_exists=False):
     """
     Build a URL to redirect the user to the Enterprise app to provide data sharing
@@ -468,9 +514,6 @@ def get_enterprise_consent_url(request, course_id, user=None, return_to=None, en
     * return_to: url name label for the page to return to after consent is granted.
                  If None, return to request.path instead.
     """
-    if not enterprise_enabled():
-        return ''
-
     user = user or request.user
 
     if not consent_needed_for_course(request, user, course_id, enrollment_exists=enrollment_exists):
@@ -499,29 +542,24 @@ def get_enterprise_consent_url(request, course_id, user=None, return_to=None, en
     return full_url
 
 
-def get_enterprise_learner_data(site, user):
+@enterprise_is_enabled()
+def get_enterprise_learner_data(user):
     """
     Client API operation adapter/wrapper
     """
-    if not enterprise_enabled():
-        return None
-
-    enterprise_learner_data = EnterpriseApiClient(user=user).fetch_enterprise_learner_data(site=site, user=user)
+    enterprise_learner_data = EnterpriseApiClient(user=user).fetch_enterprise_learner_data(user)
     if enterprise_learner_data:
         return enterprise_learner_data['results']
 
 
+@enterprise_is_enabled(otherwise={})
 def get_enterprise_customer_for_learner(site, user):
     """
     Return enterprise customer to whom given learner belongs.
     """
-    if not enterprise_enabled():
-        return {}
-
-    enterprise_learner_data = get_enterprise_learner_data(site, user)
+    enterprise_learner_data = get_enterprise_learner_data(user)
     if enterprise_learner_data:
         return enterprise_learner_data[0]['enterprise_customer']
-
     return {}
 
 
@@ -544,6 +582,7 @@ def get_consent_notification_data(enterprise_customer):
     return title_template, message_template
 
 
+@enterprise_is_enabled(otherwise='')
 def get_dashboard_consent_notification(request, user, course_enrollments):
     """
     If relevant to the request at hand, create a banner on the dashboard indicating consent failed.
@@ -556,9 +595,6 @@ def get_dashboard_consent_notification(request, user, course_enrollments):
     Returns:
         str: Either an empty string, or a string containing the HTML code for the notification banner.
     """
-    if not enterprise_enabled():
-        return ''
-
     enrollment = None
     consent_needed = False
     course_id = request.GET.get(CONSENT_FAILED_PARAMETER)
@@ -612,14 +648,12 @@ def get_dashboard_consent_notification(request, user, course_enrollments):
     return ''
 
 
+@enterprise_is_enabled()
 def insert_enterprise_pipeline_elements(pipeline):
     """
     If the enterprise app is enabled, insert additional elements into the
     pipeline related to enterprise.
     """
-    if not enterprise_enabled():
-        return
-
     additional_elements = (
         'enterprise.tpa_pipeline.handle_enterprise_logistration',
     )
diff --git a/openedx/features/enterprise_support/middleware.py b/openedx/features/enterprise_support/middleware.py
new file mode 100644
index 0000000000000000000000000000000000000000..b2b18695cc74985dbfbebe85a42db77bcdee433b
--- /dev/null
+++ b/openedx/features/enterprise_support/middleware.py
@@ -0,0 +1,29 @@
+"""
+Middleware for the Enterprise feature.
+
+The Enterprise feature must be turned on for this middleware to have any effect.
+"""
+
+from django.core.exceptions import MiddlewareNotUsed
+
+from openedx.features.enterprise_support import api
+
+
+class EnterpriseMiddleware(object):
+    """
+    Middleware that adds Enterprise-related content to the request.
+    """
+
+    def __init__(self):
+        """
+        We don't need to use this middleware if the Enterprise feature isn't enabled.
+        """
+        if not api.enterprise_enabled():
+            raise MiddlewareNotUsed()
+
+    def process_request(self, request):
+        """
+        Fill the request with Enterprise-related content.
+        """
+        if 'enterprise_customer' not in request.session and request.user.is_authenticated():
+            request.session['enterprise_customer'] = api.enterprise_customer_for_request(request)
diff --git a/openedx/features/enterprise_support/tests/__init__.py b/openedx/features/enterprise_support/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..82c57415f8049a04264ed1b199fbd70e0c3c9d90 100644
--- a/openedx/features/enterprise_support/tests/__init__.py
+++ b/openedx/features/enterprise_support/tests/__init__.py
@@ -0,0 +1,23 @@
+"""
+Things commonly needed in Enterprise tests.
+"""
+
+from django.conf import settings
+
+FEATURES_WITH_ENTERPRISE_ENABLED = settings.FEATURES.copy()
+FEATURES_WITH_ENTERPRISE_ENABLED['ENABLE_ENTERPRISE_INTEGRATION'] = True
+
+FAKE_ENTERPRISE_CUSTOMER = {
+    'active': True,
+    'branding_configuration': None,
+    'catalog': None,
+    'enable_audit_enrollment': False,
+    'enable_data_sharing_consent': False,
+    'enforce_data_sharing_consent': 'at_enrollment',
+    'enterprise_customer_entitlements': [],
+    'identity_provider': None,
+    'name': 'EnterpriseCustomer',
+    'replace_sensitive_sso_username': True,
+    'site': {'domain': 'example.com', 'name': 'example.com'},
+    'uuid': '1cbf230f-f514-4a05-845e-d57b8e29851c'
+}
diff --git a/openedx/features/enterprise_support/tests/mixins/enterprise.py b/openedx/features/enterprise_support/tests/mixins/enterprise.py
index d628640d78ae93b06fbc5dd34880d4d74d42ec89..e3016e7f6dd4940e0eb18195bd322206dddefc17 100644
--- a/openedx/features/enterprise_support/tests/mixins/enterprise.py
+++ b/openedx/features/enterprise_support/tests/mixins/enterprise.py
@@ -10,6 +10,8 @@ from django.core.cache import cache
 from django.core.urlresolvers import reverse
 from django.test import SimpleTestCase
 
+from openedx.features.enterprise_support.tests import FAKE_ENTERPRISE_CUSTOMER
+
 
 class EnterpriseServiceMockMixin(object):
     """
@@ -177,7 +179,8 @@ class EnterpriseServiceMockMixin(object):
                                 'enterprise_customer': enterprise_customer_uuid,
                                 'entitlement_id': entitlement_id
                             }
-                        ]
+                        ],
+                        'replace_sensitive_sso_username': True,
                     },
                     'user_id': 5,
                     'user': {
@@ -220,6 +223,7 @@ class EnterpriseTestConsentRequired(SimpleTestCase):
     Mixin to help test the data_sharing_consent_required decorator.
     """
 
+    @mock.patch('openedx.features.enterprise_support.api.enterprise_customer_from_api')
     @mock.patch('openedx.features.enterprise_support.api.enterprise_customer_uuid_for_request')
     @mock.patch('openedx.features.enterprise_support.api.reverse')
     @mock.patch('openedx.features.enterprise_support.api.enterprise_enabled')
@@ -232,6 +236,7 @@ class EnterpriseTestConsentRequired(SimpleTestCase):
             mock_enterprise_enabled,
             mock_reverse,
             mock_enterprise_customer_uuid_for_request,
+            mock_enterprise_customer_from_api,
             status_code=200,
     ):
         """
@@ -247,6 +252,7 @@ class EnterpriseTestConsentRequired(SimpleTestCase):
         mock_reverse.side_effect = mock_consent_reverse
         mock_enterprise_enabled.return_value = True
         mock_enterprise_customer_uuid_for_request.return_value = 'fake-uuid'
+        mock_enterprise_customer_from_api.return_value = FAKE_ENTERPRISE_CUSTOMER
         # Ensure that when consent is necessary, the user is redirected to the consent page.
         mock_consent_necessary.return_value = True
         response = client.get(url)
diff --git a/openedx/features/enterprise_support/tests/test_api.py b/openedx/features/enterprise_support/tests/test_api.py
index 554f35eb6159c1edefdaad599812b7bb6d3aea63..6a41ab97cbdb0d9e3dd5510ac0a68693c9e61150 100644
--- a/openedx/features/enterprise_support/tests/test_api.py
+++ b/openedx/features/enterprise_support/tests/test_api.py
@@ -2,8 +2,6 @@
 Test the enterprise support APIs.
 """
 
-import unittest
-
 import ddt
 import httpretty
 import mock
@@ -15,7 +13,7 @@ from django.http import HttpResponseRedirect
 from django.test.utils import override_settings
 
 from consent.models import DataSharingConsent
-from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
+from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
 from openedx.features.enterprise_support.api import (
     ConsentApiClient,
     ConsentApiServiceClient,
@@ -30,16 +28,13 @@ from openedx.features.enterprise_support.api import (
     insert_enterprise_pipeline_elements,
     enterprise_enabled,
 )
+from openedx.features.enterprise_support.tests import FEATURES_WITH_ENTERPRISE_ENABLED
 from openedx.features.enterprise_support.tests.factories import EnterpriseCustomerUserFactory
 from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseServiceMockMixin
 from openedx.features.enterprise_support.utils import get_cache_key
 from student.tests.factories import UserFactory
 
 
-FEATURES_WITH_ENTERPRISE_ENABLED = settings.FEATURES.copy()
-FEATURES_WITH_ENTERPRISE_ENABLED['ENABLE_ENTERPRISE_INTEGRATION'] = True
-
-
 class MockEnrollment(mock.MagicMock):
     """
     Mock object for an enrollment which has a consistent string representation
@@ -51,7 +46,7 @@ class MockEnrollment(mock.MagicMock):
 
 @ddt.ddt
 @override_settings(FEATURES=FEATURES_WITH_ENTERPRISE_ENABLED)
-@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
+@skip_unless_lms
 class TestEnterpriseApi(EnterpriseServiceMockMixin, CacheIsolationTestCase):
     """
     Test enterprise support APIs.
diff --git a/openedx/features/enterprise_support/tests/test_middleware.py b/openedx/features/enterprise_support/tests/test_middleware.py
new file mode 100644
index 0000000000000000000000000000000000000000..460f20192d433ded9938096b247b08abd481f247
--- /dev/null
+++ b/openedx/features/enterprise_support/tests/test_middleware.py
@@ -0,0 +1,66 @@
+"""
+Tests for Enterprise middleware.
+"""
+
+import mock
+
+from django.core.urlresolvers import reverse
+from django.test import TestCase
+from django.test.utils import override_settings
+
+from openedx.core.djangolib.testing.utils import skip_unless_lms
+from openedx.features.enterprise_support.tests import (
+    FAKE_ENTERPRISE_CUSTOMER, FEATURES_WITH_ENTERPRISE_ENABLED,
+    factories
+)
+from student.tests.factories import UserFactory
+
+
+@override_settings(FEATURES=FEATURES_WITH_ENTERPRISE_ENABLED)
+@skip_unless_lms
+class EnterpriseMiddlewareTest(TestCase):
+    """
+    Test for `EnterpriseMiddleware`.
+    """
+
+    def setUp(self):
+        """Initiate commonly needed objects."""
+        super(EnterpriseMiddlewareTest, self).setUp()
+
+        # Customer & Learner details.
+        self.user = UserFactory.create(username='username', password='password')
+        self.enterprise_customer = FAKE_ENTERPRISE_CUSTOMER
+        self.enterprise_learner = factories.EnterpriseCustomerUserFactory(user_id=self.user.id)
+
+        # Request details.
+        self.client.login(username='username', password='password')
+        self.dashboard = reverse('dashboard')
+
+        # Mocks.
+        patcher = mock.patch('openedx.features.enterprise_support.api.enterprise_customer_from_api')
+        self.mock_enterprise_customer_from_api = patcher.start()
+        self.mock_enterprise_customer_from_api.return_value = self.enterprise_customer
+        self.addCleanup(patcher.stop)
+
+    def test_anonymous_user(self):
+        """The `enterprise_customer` is not set in the session if the user is anonymous."""
+        self.client.logout()
+        self.client.get(self.dashboard)
+        assert self.client.session.get('enterprise_customer') is None
+
+    def test_enterprise_customer(self):
+        """The `enterprise_customer` gets set in the session."""
+        self.client.get(self.dashboard)
+        assert self.client.session.get('enterprise_customer') == self.enterprise_customer
+
+    def test_enterprise_customer_cached(self):
+        """The middleware doesn't attempt to refill `enterprise_customer` if it already exists in the session."""
+        assert not self.mock_enterprise_customer_from_api.called
+
+        # First call populates the session by calling the API.
+        self.client.get(self.dashboard)
+        assert self.mock_enterprise_customer_from_api.call_count == 1
+
+        # Second same call has no need to call the API because the session already contains the data.
+        self.client.get(self.dashboard)
+        assert self.mock_enterprise_customer_from_api.call_count == 1
diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt
index 813bf5ee8763e6dfd5808c1f2e1abbeb5456fe0d..4d8d0952bd28d1b43ea3ee00706c69a9a04f5e87 100644
--- a/requirements/edx/base.txt
+++ b/requirements/edx/base.txt
@@ -115,13 +115,13 @@ edx-django-oauth2-provider==1.2.5
 edx-django-release-util==0.3.1
 edx-django-sites-extensions==2.3.1
 edx-drf-extensions==1.2.5
-edx-enterprise==0.67.5
+edx-enterprise==0.67.6
 edx-i18n-tools==0.4.4
 edx-milestones==0.1.13
 edx-oauth2-provider==1.2.2
 edx-opaque-keys[django]==0.4.4
 edx-organizations==0.4.10
-edx-proctoring==1.3.9
+edx-proctoring==1.4.0
 edx-rest-api-client==1.7.1
 edx-search==1.1.0
 edx-submissions==2.0.12
diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt
index 1111e4f0ba8043369a13e2683de18f4356333b97..7fbc6f2be88a51ab211f57403fa77a2a7ac3aa9a 100644
--- a/requirements/edx/development.txt
+++ b/requirements/edx/development.txt
@@ -134,14 +134,14 @@ edx-django-oauth2-provider==1.2.5
 edx-django-release-util==0.3.1
 edx-django-sites-extensions==2.3.1
 edx-drf-extensions==1.2.5
-edx-enterprise==0.67.5
+edx-enterprise==0.67.6
 edx-i18n-tools==0.4.4
 edx-lint==0.5.4
 edx-milestones==0.1.13
 edx-oauth2-provider==1.2.2
 edx-opaque-keys[django]==0.4.4
 edx-organizations==0.4.10
-edx-proctoring==1.3.9
+edx-proctoring==1.4.0
 edx-rest-api-client==1.7.1
 edx-search==1.1.0
 edx-sphinx-theme==1.3.0
@@ -328,4 +328,4 @@ xblock-review==1.1.5
 xblock==1.1.1
 xmltodict==0.4.1
 zendesk==1.1.1
-zope.interface==4.4.3
+zope.interface==4.5.0
diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt
index a202e693f278952db89f7146b5bb5700492aa543..e3bae8fb261f4ef92ea34c8b85a4889cdc161945 100644
--- a/requirements/edx/testing.txt
+++ b/requirements/edx/testing.txt
@@ -129,14 +129,14 @@ edx-django-oauth2-provider==1.2.5
 edx-django-release-util==0.3.1
 edx-django-sites-extensions==2.3.1
 edx-drf-extensions==1.2.5
-edx-enterprise==0.67.5
+edx-enterprise==0.67.6
 edx-i18n-tools==0.4.4
 edx-lint==0.5.4
 edx-milestones==0.1.13
 edx-oauth2-provider==1.2.2
 edx-opaque-keys[django]==0.4.4
 edx-organizations==0.4.10
-edx-proctoring==1.3.9
+edx-proctoring==1.4.0
 edx-rest-api-client==1.7.1
 edx-search==1.1.0
 edx-submissions==2.0.12
@@ -311,4 +311,4 @@ xblock-review==1.1.5
 xblock==1.1.1
 xmltodict==0.4.1
 zendesk==1.1.1
-zope.interface==4.4.3     # via twisted
+zope.interface==4.5.0     # via twisted