Commit 1189867d by Clinton Blackburn Committed by Clinton Blackburn

Removed references to ECOMMERCE_API_SIGNING_KEY

We should not be using custom signing keys for each service at this time. We may want to return to this strategy in the future; but, this is not the direction any of our other services are going in.

ECOM-6541
parent e5112bd3
...@@ -49,7 +49,6 @@ import request_cache ...@@ -49,7 +49,6 @@ import request_cache
from certificates.models import GeneratedCertificate from certificates.models import GeneratedCertificate
from course_modes.models import CourseMode from course_modes.models import CourseMode
from enrollment.api import _default_course_mode from enrollment.api import _default_course_mode
from openedx.core.djangoapps.commerce.utils import ecommerce_api_client, ECOMMERCE_DATE_FORMAT
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
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.djangoapps.xmodule_django.models import CourseKeyField, NoneToEmptyManager from openedx.core.djangoapps.xmodule_django.models import CourseKeyField, NoneToEmptyManager
...@@ -1537,6 +1536,9 @@ class CourseEnrollment(models.Model): ...@@ -1537,6 +1536,9 @@ class CourseEnrollment(models.Model):
def refund_cutoff_date(self): def refund_cutoff_date(self):
""" Calculate and return the refund window end date. """ """ Calculate and return the refund window end date. """
# NOTE: This is here to avoid circular references
from openedx.core.djangoapps.commerce.utils import ecommerce_api_client, ECOMMERCE_DATE_FORMAT
try: try:
attribute = self.attributes.get(namespace='order', name='order_number') attribute = self.attributes.get(namespace='order', name='order_number')
except ObjectDoesNotExist: except ObjectDoesNotExist:
......
...@@ -30,7 +30,6 @@ from config_models.models import cache ...@@ -30,7 +30,6 @@ from config_models.models import cache
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
TEST_API_URL = 'http://www-internal.example.com/api' TEST_API_URL = 'http://www-internal.example.com/api'
TEST_API_SIGNING_KEY = 'edx'
JSON = 'application/json' JSON = 'application/json'
...@@ -131,7 +130,7 @@ class RefundableTest(SharedModuleStoreTestCase): ...@@ -131,7 +130,7 @@ class RefundableTest(SharedModuleStoreTestCase):
) )
@ddt.unpack @ddt.unpack
@httpretty.activate @httpretty.activate
@override_settings(ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY, ECOMMERCE_API_URL=TEST_API_URL) @override_settings(ECOMMERCE_API_URL=TEST_API_URL)
def test_refund_cutoff_date(self, order_date_delta, course_start_delta, expected_date_delta, days): def test_refund_cutoff_date(self, order_date_delta, course_start_delta, expected_date_delta, days):
""" """
Assert that the later date is used with the configurable refund period in calculating the returned cutoff date. Assert that the later date is used with the configurable refund period in calculating the returned cutoff date.
...@@ -172,7 +171,7 @@ class RefundableTest(SharedModuleStoreTestCase): ...@@ -172,7 +171,7 @@ class RefundableTest(SharedModuleStoreTestCase):
self.assertIsNone(self.enrollment.refund_cutoff_date()) self.assertIsNone(self.enrollment.refund_cutoff_date())
@httpretty.activate @httpretty.activate
@override_settings(ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY, ECOMMERCE_API_URL=TEST_API_URL) @override_settings(ECOMMERCE_API_URL=TEST_API_URL)
def test_multiple_refunds_dashbaord_page_error(self): def test_multiple_refunds_dashbaord_page_error(self):
""" Order with mutiple refunds will not throw 500 error when dashboard page will access.""" """ Order with mutiple refunds will not throw 500 error when dashboard page will access."""
now = datetime.now(pytz.UTC).replace(microsecond=0) now = datetime.now(pytz.UTC).replace(microsecond=0)
......
...@@ -18,7 +18,7 @@ from social import actions, exceptions ...@@ -18,7 +18,7 @@ from social import actions, exceptions
from social.apps.django_app import utils as social_utils from social.apps.django_app import utils as social_utils
from social.apps.django_app import views as social_views from social.apps.django_app import views as social_views
from lms.djangoapps.commerce.tests import TEST_API_URL, TEST_API_SIGNING_KEY from lms.djangoapps.commerce.tests import TEST_API_URL
from student import models as student_models from student import models as student_models
from student import views as student_views from student import views as student_views
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
...@@ -911,7 +911,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase): ...@@ -911,7 +911,7 @@ class IntegrationTest(testutil.TestCase, test.TestCase):
# pylint: disable=test-inherits-tests, abstract-method # pylint: disable=test-inherits-tests, abstract-method
@django_utils.override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY) @django_utils.override_settings(ECOMMERCE_API_URL=TEST_API_URL)
class Oauth2IntegrationTest(IntegrationTest): class Oauth2IntegrationTest(IntegrationTest):
"""Base test case for integration tests of Oauth2 providers.""" """Base test case for integration tests of Oauth2 providers."""
......
...@@ -17,7 +17,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase ...@@ -17,7 +17,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.factories import CourseFactory
from commerce.constants import Messages from commerce.constants import Messages
from commerce.tests import TEST_BASKET_ID, TEST_ORDER_NUMBER, TEST_PAYMENT_DATA, TEST_API_URL, TEST_API_SIGNING_KEY from commerce.tests import TEST_BASKET_ID, TEST_ORDER_NUMBER, TEST_PAYMENT_DATA
from commerce.tests.mocks import mock_basket_order, mock_create_basket from commerce.tests.mocks import mock_basket_order, mock_create_basket
from commerce.tests.test_views import UserMixin from commerce.tests.test_views import UserMixin
from course_modes.models import CourseMode from course_modes.models import CourseMode
...@@ -39,7 +39,6 @@ UTM_COOKIE_CONTENTS = { ...@@ -39,7 +39,6 @@ UTM_COOKIE_CONTENTS = {
@attr(shard=1) @attr(shard=1)
@ddt.ddt @ddt.ddt
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY)
class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase): class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase):
""" """
Tests for the commerce orders view. Tests for the commerce orders view.
...@@ -276,7 +275,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase) ...@@ -276,7 +275,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
# We should be enrolled in honor mode # We should be enrolled in honor mode
self._test_course_without_sku(enrollment_mode=CourseMode.HONOR) self._test_course_without_sku(enrollment_mode=CourseMode.HONOR)
@override_settings(ECOMMERCE_API_URL=None, ECOMMERCE_API_SIGNING_KEY=None) @override_settings(ECOMMERCE_API_URL=None)
def test_ecommerce_service_not_configured(self): def test_ecommerce_service_not_configured(self):
""" """
If the E-Commerce Service is not configured, the view should enroll the user. If the E-Commerce Service is not configured, the view should enroll the user.
...@@ -313,7 +312,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase) ...@@ -313,7 +312,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
""" Verifies that the view behaves appropriately when the course only has a professional mode. """ """ Verifies that the view behaves appropriately when the course only has a professional mode. """
self.assertProfessionalModeBypassed() self.assertProfessionalModeBypassed()
@override_settings(ECOMMERCE_API_URL=None, ECOMMERCE_API_SIGNING_KEY=None) @override_settings(ECOMMERCE_API_URL=None)
def test_professional_mode_only_and_ecommerce_service_not_configured(self): def test_professional_mode_only_and_ecommerce_service_not_configured(self):
""" """
Verifies that the view behaves appropriately when the course only has a professional mode and Verifies that the view behaves appropriately when the course only has a professional mode and
...@@ -390,7 +389,6 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase) ...@@ -390,7 +389,6 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
@attr(shard=1) @attr(shard=1)
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY)
class BasketOrderViewTests(UserMixin, TestCase): class BasketOrderViewTests(UserMixin, TestCase):
""" Tests for the basket order view. """ """ Tests for the basket order view. """
view_name = 'commerce_api:v0:baskets:retrieve_order' view_name = 'commerce_api:v0:baskets:retrieve_order'
......
...@@ -17,7 +17,6 @@ from rest_framework.utils.encoders import JSONEncoder ...@@ -17,7 +17,6 @@ from rest_framework.utils.encoders import JSONEncoder
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.factories import CourseFactory
from commerce.tests import TEST_API_URL, TEST_API_SIGNING_KEY
from commerce.tests.mocks import mock_order_endpoint from commerce.tests.mocks import mock_order_endpoint
from commerce.tests.test_views import UserMixin from commerce.tests.test_views import UserMixin
from course_modes.models import CourseMode from course_modes.models import CourseMode
...@@ -391,7 +390,6 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase) ...@@ -391,7 +390,6 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase)
@attr(shard=1) @attr(shard=1)
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY)
class OrderViewTests(UserMixin, TestCase): class OrderViewTests(UserMixin, TestCase):
""" Tests for the basket order view. """ """ Tests for the basket order view. """
view_name = 'commerce_api:v1:orders:detail' view_name = 'commerce_api:v1:orders:detail'
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" Commerce app tests package. """ """ Commerce app tests package. """
import datetime
import json
import httpretty
import mock
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings
from freezegun import freeze_time from freezegun import freeze_time
import httpretty
import jwt
import mock
from edx_rest_api_client import auth
from openedx.core.djangoapps.commerce.utils import ecommerce_api_client from openedx.core.djangoapps.commerce.utils import ecommerce_api_client
from openedx.core.lib.token_utils import JwtBuilder
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
JSON = 'application/json' JSON = 'application/json'
TEST_PUBLIC_URL_ROOT = 'http://www.example.com' TEST_PUBLIC_URL_ROOT = 'http://www.example.com'
TEST_API_URL = 'http://www-internal.example.com/api' TEST_API_URL = 'http://www-internal.example.com/api'
TEST_API_SIGNING_KEY = 'edx'
TEST_BASKET_ID = 7 TEST_BASKET_ID = 7
TEST_ORDER_NUMBER = '100004' TEST_ORDER_NUMBER = '100004'
TEST_PAYMENT_DATA = { TEST_PAYMENT_DATA = {
...@@ -29,33 +23,27 @@ TEST_PAYMENT_DATA = { ...@@ -29,33 +23,27 @@ TEST_PAYMENT_DATA = {
} }
@override_settings(ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY, ECOMMERCE_API_URL=TEST_API_URL)
class EdxRestApiClientTest(TestCase): class EdxRestApiClientTest(TestCase):
""" Tests to ensure the client is initialized properly. """ """ Tests to ensure the client is initialized properly. """
TEST_USER_EMAIL = 'test@example.com'
TEST_CLIENT_ID = 'test-client-id' TEST_CLIENT_ID = 'test-client-id'
def setUp(self): def setUp(self):
super(EdxRestApiClientTest, self).setUp() super(EdxRestApiClientTest, self).setUp()
self.user = UserFactory() self.user = UserFactory()
self.user.email = self.TEST_USER_EMAIL
self.user.save() # pylint: disable=no-member
@httpretty.activate @httpretty.activate
@freeze_time('2015-7-2') @freeze_time('2015-7-2')
@override_settings(JWT_AUTH={'JWT_ISSUER': 'http://example.com/oauth', 'JWT_EXPIRATION': 30})
def test_tracking_context(self): def test_tracking_context(self):
""" """
Ensure the tracking context is set up in the api client correctly and Ensure the tracking context is set up in the api client correctly and
automatically. automatically.
""" """
# fake an ecommerce api request. # fake an E-Commerce API request.
httpretty.register_uri( httpretty.register_uri(
httpretty.POST, httpretty.POST,
'{}/baskets/1/'.format(TEST_API_URL), '{}/baskets/1/'.format(settings.ECOMMERCE_API_URL.strip('/')),
status=200, body='{}', status=200, body='{}',
adding_headers={'Content-Type': JSON} adding_headers={'Content-Type': JSON}
) )
...@@ -65,23 +53,18 @@ class EdxRestApiClientTest(TestCase): ...@@ -65,23 +53,18 @@ class EdxRestApiClientTest(TestCase):
with mock.patch('openedx.core.djangoapps.commerce.utils.tracker.get_tracker', return_value=mock_tracker): with mock.patch('openedx.core.djangoapps.commerce.utils.tracker.get_tracker', return_value=mock_tracker):
ecommerce_api_client(self.user).baskets(1).post() ecommerce_api_client(self.user).baskets(1).post()
# make sure the request's JWT token payload included correct tracking context values. # Verify the JWT includes the tracking context for the user
actual_header = httpretty.last_request().headers['Authorization'] actual_header = httpretty.last_request().headers['Authorization']
expected_payload = {
'username': self.user.username, claims = {
'full_name': self.user.profile.name,
'email': self.user.email,
'iss': settings.JWT_AUTH['JWT_ISSUER'],
'iat': datetime.datetime.utcnow(),
'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=settings.JWT_AUTH['JWT_EXPIRATION']),
'tracking_context': { 'tracking_context': {
'lms_user_id': self.user.id, # pylint: disable=no-member 'lms_user_id': self.user.id, # pylint: disable=no-member
'lms_client_id': self.TEST_CLIENT_ID, 'lms_client_id': self.TEST_CLIENT_ID,
'lms_ip': '127.0.0.1', 'lms_ip': '127.0.0.1',
}, }
} }
expected_jwt = JwtBuilder(self.user).build_token(['email', 'profile'], additional_claims=claims)
expected_header = 'JWT {}'.format(jwt.encode(expected_payload, TEST_API_SIGNING_KEY)) expected_header = 'JWT {}'.format(expected_jwt)
self.assertEqual(actual_header, expected_header) self.assertEqual(actual_header, expected_header)
@httpretty.activate @httpretty.activate
...@@ -95,19 +78,9 @@ class EdxRestApiClientTest(TestCase): ...@@ -95,19 +78,9 @@ class EdxRestApiClientTest(TestCase):
expected_content = '{"result": "Préparatoire"}' expected_content = '{"result": "Préparatoire"}'
httpretty.register_uri( httpretty.register_uri(
httpretty.GET, httpretty.GET,
'{}/baskets/1/order/'.format(TEST_API_URL), '{}/baskets/1/order/'.format(settings.ECOMMERCE_API_URL.strip('/')),
status=200, body=expected_content, status=200, body=expected_content,
adding_headers={'Content-Type': JSON}, adding_headers={'Content-Type': JSON},
) )
actual_object = ecommerce_api_client(self.user).baskets(1).order.get() actual_object = ecommerce_api_client(self.user).baskets(1).order.get()
self.assertEqual(actual_object, {u"result": u"Préparatoire"}) self.assertEqual(actual_object, {u"result": u"Préparatoire"})
def test_client_with_user_without_profile(self):
"""
Verify client initialize successfully for users having no profile.
"""
worker = User.objects.create_user(username='test_worker', email='test@example.com')
api_client = ecommerce_api_client(worker)
self.assertEqual(api_client._store['session'].auth.__dict__['username'], worker.username) # pylint: disable=protected-access
self.assertIsNone(api_client._store['session'].auth.__dict__['full_name']) # pylint: disable=protected-access
...@@ -2,11 +2,14 @@ ...@@ -2,11 +2,14 @@
import json import json
import httpretty import httpretty
from django.conf import settings
from commerce.tests import TEST_API_URL, factories from commerce.tests import factories
class mock_ecommerce_api_endpoint(object): # pylint: disable=invalid-name # pylint: disable=invalid-name
class mock_ecommerce_api_endpoint(object):
""" """
Base class for contextmanagers used to mock calls to api endpoints. Base class for contextmanagers used to mock calls to api endpoints.
...@@ -21,6 +24,8 @@ class mock_ecommerce_api_endpoint(object): # pylint: disable=invalid-name ...@@ -21,6 +24,8 @@ class mock_ecommerce_api_endpoint(object): # pylint: disable=invalid-name
# override this in subclasses, using one of httpretty's method constants # override this in subclasses, using one of httpretty's method constants
method = None method = None
host = settings.ECOMMERCE_API_URL.strip('/')
def __init__(self, response=None, status=200, expect_called=True, exception=None): def __init__(self, response=None, status=200, expect_called=True, exception=None):
""" """
Keyword Arguments: Keyword Arguments:
...@@ -37,9 +42,18 @@ class mock_ecommerce_api_endpoint(object): # pylint: disable=invalid-name ...@@ -37,9 +42,18 @@ class mock_ecommerce_api_endpoint(object): # pylint: disable=invalid-name
def get_uri(self): def get_uri(self):
""" """
Return the uri to register with httpretty for this contextmanager. Returns the uri to register with httpretty for this contextmanager.
"""
return self.host + '/' + self.get_path().lstrip('/')
def get_path(self):
"""
Returns the path of the URI to register with httpretty for this contextmanager.
Subclasses must override this method. Subclasses must override this method.
Returns:
str
""" """
raise NotImplementedError raise NotImplementedError
...@@ -48,7 +62,6 @@ class mock_ecommerce_api_endpoint(object): # pylint: disable=invalid-name ...@@ -48,7 +62,6 @@ class mock_ecommerce_api_endpoint(object): # pylint: disable=invalid-name
raise self.exception # pylint: disable=raising-bad-type raise self.exception # pylint: disable=raising-bad-type
def __enter__(self): def __enter__(self):
httpretty.reset()
httpretty.enable() httpretty.enable()
httpretty.register_uri( httpretty.register_uri(
self.method, self.method,
...@@ -61,9 +74,10 @@ class mock_ecommerce_api_endpoint(object): # pylint: disable=invalid-name ...@@ -61,9 +74,10 @@ class mock_ecommerce_api_endpoint(object): # pylint: disable=invalid-name
def __exit__(self, exc_type, exc_val, exc_tb): def __exit__(self, exc_type, exc_val, exc_tb):
assert self.expect_called == (httpretty.last_request().headers != {}) assert self.expect_called == (httpretty.last_request().headers != {})
httpretty.disable() httpretty.disable()
httpretty.reset()
class mock_create_basket(mock_ecommerce_api_endpoint): # pylint: disable=invalid-name class mock_create_basket(mock_ecommerce_api_endpoint):
""" Mocks calls to E-Commerce API client basket creation method. """ """ Mocks calls to E-Commerce API client basket creation method. """
default_response = { default_response = {
...@@ -77,11 +91,11 @@ class mock_create_basket(mock_ecommerce_api_endpoint): # pylint: disable=invali ...@@ -77,11 +91,11 @@ class mock_create_basket(mock_ecommerce_api_endpoint): # pylint: disable=invali
} }
method = httpretty.POST method = httpretty.POST
def get_uri(self): def get_path(self):
return TEST_API_URL + '/baskets/' return '/baskets/'
class mock_basket_order(mock_ecommerce_api_endpoint): # pylint: disable=invalid-name class mock_basket_order(mock_ecommerce_api_endpoint):
""" Mocks calls to E-Commerce API client basket order method. """ """ Mocks calls to E-Commerce API client basket order method. """
default_response = {'number': 1} default_response = {'number': 1}
...@@ -91,21 +105,21 @@ class mock_basket_order(mock_ecommerce_api_endpoint): # pylint: disable=invalid ...@@ -91,21 +105,21 @@ class mock_basket_order(mock_ecommerce_api_endpoint): # pylint: disable=invalid
super(mock_basket_order, self).__init__(**kwargs) super(mock_basket_order, self).__init__(**kwargs)
self.basket_id = basket_id self.basket_id = basket_id
def get_uri(self): def get_path(self):
return TEST_API_URL + '/baskets/{}/order/'.format(self.basket_id) return '/baskets/{}/order/'.format(self.basket_id)
class mock_create_refund(mock_ecommerce_api_endpoint): # pylint: disable=invalid-name class mock_create_refund(mock_ecommerce_api_endpoint):
""" Mocks calls to E-Commerce API client refund creation method. """ """ Mocks calls to E-Commerce API client refund creation method. """
default_response = [] default_response = []
method = httpretty.POST method = httpretty.POST
def get_uri(self): def get_path(self):
return TEST_API_URL + '/refunds/' return '/refunds/'
class mock_order_endpoint(mock_ecommerce_api_endpoint): # pylint: disable=invalid-name class mock_order_endpoint(mock_ecommerce_api_endpoint):
""" Mocks calls to E-Commerce API client basket order method. """ """ Mocks calls to E-Commerce API client basket order method. """
default_response = {'number': 'EDX-100001'} default_response = {'number': 'EDX-100001'}
...@@ -115,11 +129,11 @@ class mock_order_endpoint(mock_ecommerce_api_endpoint): # pylint: disable=inval ...@@ -115,11 +129,11 @@ class mock_order_endpoint(mock_ecommerce_api_endpoint): # pylint: disable=inval
super(mock_order_endpoint, self).__init__(**kwargs) super(mock_order_endpoint, self).__init__(**kwargs)
self.order_number = order_number self.order_number = order_number
def get_uri(self): def get_path(self):
return TEST_API_URL + '/orders/{}/'.format(self.order_number) return '/orders/{}/'.format(self.order_number)
class mock_get_orders(mock_ecommerce_api_endpoint): # pylint: disable=invalid-name class mock_get_orders(mock_ecommerce_api_endpoint):
""" Mocks calls to E-Commerce API client order get method. """ """ Mocks calls to E-Commerce API client order get method. """
default_response = { default_response = {
...@@ -138,5 +152,5 @@ class mock_get_orders(mock_ecommerce_api_endpoint): # pylint: disable=invalid-n ...@@ -138,5 +152,5 @@ class mock_get_orders(mock_ecommerce_api_endpoint): # pylint: disable=invalid-n
} }
method = httpretty.GET method = httpretty.GET
def get_uri(self): def get_path(self):
return TEST_API_URL + '/orders/' return '/orders/'
...@@ -9,21 +9,22 @@ import json ...@@ -9,21 +9,22 @@ import json
from urlparse import urljoin from urlparse import urljoin
import ddt import ddt
import httpretty
import mock
from django.contrib.auth.models import AnonymousUser from django.contrib.auth.models import AnonymousUser
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
import httpretty
import mock
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from requests import Timeout from requests import Timeout
from student.models import UNENROLL_DONE from commerce.signals import (
from student.tests.factories import UserFactory, CourseEnrollmentFactory refund_seat, send_refund_notification, generate_refund_notification_body, create_zendesk_ticket
from commerce.signals import (refund_seat, send_refund_notification, generate_refund_notification_body, )
create_zendesk_ticket) from commerce.tests import JSON
from commerce.tests import TEST_PUBLIC_URL_ROOT, TEST_API_URL, TEST_API_SIGNING_KEY, JSON
from commerce.tests.mocks import mock_create_refund from commerce.tests.mocks import mock_create_refund
from course_modes.models import CourseMode from course_modes.models import CourseMode
from student.models import UNENROLL_DONE
from student.tests.factories import UserFactory, CourseEnrollmentFactory
ZENDESK_URL = 'http://zendesk.example.com/' ZENDESK_URL = 'http://zendesk.example.com/'
ZENDESK_USER = 'test@example.com' ZENDESK_USER = 'test@example.com'
...@@ -31,11 +32,7 @@ ZENDESK_API_KEY = 'abc123' ...@@ -31,11 +32,7 @@ ZENDESK_API_KEY = 'abc123'
@ddt.ddt @ddt.ddt
@override_settings( @override_settings(ZENDESK_URL=ZENDESK_URL, ZENDESK_USER=ZENDESK_USER, ZENDESK_API_KEY=ZENDESK_API_KEY)
ECOMMERCE_PUBLIC_URL_ROOT=TEST_PUBLIC_URL_ROOT,
ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY,
ZENDESK_URL=ZENDESK_URL, ZENDESK_USER=ZENDESK_USER, ZENDESK_API_KEY=ZENDESK_API_KEY
)
class TestRefundSignal(TestCase): class TestRefundSignal(TestCase):
""" """
Exercises logic triggered by the UNENROLL_DONE signal. Exercises logic triggered by the UNENROLL_DONE signal.
...@@ -65,7 +62,6 @@ class TestRefundSignal(TestCase): ...@@ -65,7 +62,6 @@ class TestRefundSignal(TestCase):
@override_settings( @override_settings(
ECOMMERCE_PUBLIC_URL_ROOT=None, ECOMMERCE_PUBLIC_URL_ROOT=None,
ECOMMERCE_API_URL=None, ECOMMERCE_API_URL=None,
ECOMMERCE_API_SIGNING_KEY=None,
) )
def test_no_service(self): def test_no_service(self):
""" """
......
...@@ -32,7 +32,7 @@ from provider.oauth2.models import ( ...@@ -32,7 +32,7 @@ from provider.oauth2.models import (
from testfixtures import LogCapture from testfixtures import LogCapture
from commerce.models import CommerceConfiguration from commerce.models import CommerceConfiguration
from commerce.tests import TEST_API_URL, TEST_API_SIGNING_KEY, factories from commerce.tests import factories
from commerce.tests.mocks import mock_get_orders from commerce.tests.mocks import mock_get_orders
from course_modes.models import CourseMode from course_modes.models import CourseMode
from openedx.core.djangoapps.oauth_dispatch.tests import factories as dot_factories from openedx.core.djangoapps.oauth_dispatch.tests import factories as dot_factories
...@@ -506,7 +506,6 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi ...@@ -506,7 +506,6 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
}) })
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY)
class AccountSettingsViewTest(ThirdPartyAuthTestMixin, TestCase, ProgramsApiConfigMixin): class AccountSettingsViewTest(ThirdPartyAuthTestMixin, TestCase, ProgramsApiConfigMixin):
""" Tests for the account settings view. """ """ Tests for the account settings view. """
......
...@@ -36,7 +36,7 @@ from course_modes.tests.factories import CourseModeFactory ...@@ -36,7 +36,7 @@ from course_modes.tests.factories import CourseModeFactory
from courseware.url_helpers import get_redirect_url from courseware.url_helpers import get_redirect_url
from common.test.utils import XssTestMixin from common.test.utils import XssTestMixin
from commerce.models import CommerceConfiguration from commerce.models import CommerceConfiguration
from commerce.tests import TEST_PAYMENT_DATA, TEST_API_URL, TEST_API_SIGNING_KEY, TEST_PUBLIC_URL_ROOT from commerce.tests import TEST_PAYMENT_DATA, TEST_API_URL, TEST_PUBLIC_URL_ROOT
from openedx.core.djangoapps.embargo.test_utils import restrict_course from openedx.core.djangoapps.embargo.test_utils import restrict_course
from openedx.core.djangoapps.user_api.accounts.api import get_account_settings from openedx.core.djangoapps.user_api.accounts.api import get_account_settings
from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme
...@@ -140,7 +140,6 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin): ...@@ -140,7 +140,6 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
@httpretty.activate @httpretty.activate
@override_settings( @override_settings(
ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_URL=TEST_API_URL,
ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY,
ECOMMERCE_PUBLIC_URL_ROOT=TEST_PUBLIC_URL_ROOT ECOMMERCE_PUBLIC_URL_ROOT=TEST_PUBLIC_URL_ROOT
) )
def test_start_flow_with_ecommerce(self): def test_start_flow_with_ecommerce(self):
...@@ -1053,7 +1052,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin): ...@@ -1053,7 +1052,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
self.assertEqual(response_dict['course_name'], mode_display_name) self.assertEqual(response_dict['course_name'], mode_display_name)
@httpretty.activate @httpretty.activate
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY) @override_settings(ECOMMERCE_API_URL=TEST_API_URL)
@ddt.data("verify_student_start_flow", "verify_student_begin_flow") @ddt.data("verify_student_start_flow", "verify_student_begin_flow")
def test_processors_api(self, payment_flow): def test_processors_api(self, payment_flow):
""" """
...@@ -1223,7 +1222,7 @@ class TestCreateOrderShoppingCart(CheckoutTestMixin, ModuleStoreTestCase): ...@@ -1223,7 +1222,7 @@ class TestCreateOrderShoppingCart(CheckoutTestMixin, ModuleStoreTestCase):
@attr(shard=2) @attr(shard=2)
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY) @override_settings(ECOMMERCE_API_URL=TEST_API_URL)
@patch( @patch(
'lms.djangoapps.verify_student.views.checkout_with_ecommerce_service', 'lms.djangoapps.verify_student.views.checkout_with_ecommerce_service',
return_value=TEST_PAYMENT_DATA, return_value=TEST_PAYMENT_DATA,
...@@ -1248,7 +1247,7 @@ class TestCheckoutWithEcommerceService(ModuleStoreTestCase): ...@@ -1248,7 +1247,7 @@ class TestCheckoutWithEcommerceService(ModuleStoreTestCase):
""" """
@httpretty.activate @httpretty.activate
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY) @override_settings(ECOMMERCE_API_URL=TEST_API_URL)
def test_create_basket(self): def test_create_basket(self):
""" """
Check that when working with a product being processed by the Check that when working with a product being processed by the
......
...@@ -776,7 +776,6 @@ ONLOAD_BEACON_SAMPLE_RATE = ENV_TOKENS.get('ONLOAD_BEACON_SAMPLE_RATE', ONLOAD_B ...@@ -776,7 +776,6 @@ ONLOAD_BEACON_SAMPLE_RATE = ENV_TOKENS.get('ONLOAD_BEACON_SAMPLE_RATE', ONLOAD_B
##### ECOMMERCE API CONFIGURATION SETTINGS ##### ##### ECOMMERCE API CONFIGURATION SETTINGS #####
ECOMMERCE_PUBLIC_URL_ROOT = ENV_TOKENS.get('ECOMMERCE_PUBLIC_URL_ROOT', ECOMMERCE_PUBLIC_URL_ROOT) ECOMMERCE_PUBLIC_URL_ROOT = ENV_TOKENS.get('ECOMMERCE_PUBLIC_URL_ROOT', ECOMMERCE_PUBLIC_URL_ROOT)
ECOMMERCE_API_URL = ENV_TOKENS.get('ECOMMERCE_API_URL', ECOMMERCE_API_URL) ECOMMERCE_API_URL = ENV_TOKENS.get('ECOMMERCE_API_URL', ECOMMERCE_API_URL)
ECOMMERCE_API_SIGNING_KEY = AUTH_TOKENS.get('ECOMMERCE_API_SIGNING_KEY', ECOMMERCE_API_SIGNING_KEY)
ECOMMERCE_API_TIMEOUT = ENV_TOKENS.get('ECOMMERCE_API_TIMEOUT', ECOMMERCE_API_TIMEOUT) ECOMMERCE_API_TIMEOUT = ENV_TOKENS.get('ECOMMERCE_API_TIMEOUT', ECOMMERCE_API_TIMEOUT)
COURSE_CATALOG_API_URL = ENV_TOKENS.get('COURSE_CATALOG_API_URL', COURSE_CATALOG_API_URL) COURSE_CATALOG_API_URL = ENV_TOKENS.get('COURSE_CATALOG_API_URL', COURSE_CATALOG_API_URL)
......
...@@ -218,7 +218,6 @@ BADGING_BACKEND = 'lms.djangoapps.badges.backends.tests.dummy_backend.DummyBacke ...@@ -218,7 +218,6 @@ BADGING_BACKEND = 'lms.djangoapps.badges.backends.tests.dummy_backend.DummyBacke
# Configure the LMS to use our stub eCommerce implementation # Configure the LMS to use our stub eCommerce implementation
ECOMMERCE_API_URL = 'http://localhost:8043/api/v2/' ECOMMERCE_API_URL = 'http://localhost:8043/api/v2/'
ECOMMERCE_API_SIGNING_KEY = 'ecommerce-key'
LMS_ROOT_URL = "http://localhost:8000" LMS_ROOT_URL = "http://localhost:8000"
DOC_LINK_BASE_URL = 'http://edx.readthedocs.io/projects/edx-guide-for-students' DOC_LINK_BASE_URL = 'http://edx.readthedocs.io/projects/edx-guide-for-students'
......
...@@ -2831,7 +2831,6 @@ ACCOUNT_VISIBILITY_CONFIGURATION = { ...@@ -2831,7 +2831,6 @@ ACCOUNT_VISIBILITY_CONFIGURATION = {
# E-Commerce API Configuration # E-Commerce API Configuration
ECOMMERCE_PUBLIC_URL_ROOT = None ECOMMERCE_PUBLIC_URL_ROOT = None
ECOMMERCE_API_URL = None ECOMMERCE_API_URL = None
ECOMMERCE_API_SIGNING_KEY = None
ECOMMERCE_API_TIMEOUT = 5 ECOMMERCE_API_TIMEOUT = 5
ECOMMERCE_SERVICE_WORKER_USERNAME = 'ecommerce_worker' ECOMMERCE_SERVICE_WORKER_USERNAME = 'ecommerce_worker'
ENTERPRISE_SERVICE_WORKER_USERNAME = 'enterprise_worker' ENTERPRISE_SERVICE_WORKER_USERNAME = 'enterprise_worker'
......
...@@ -588,3 +588,5 @@ COMPREHENSIVE_THEME_DIRS = [REPO_ROOT / "themes", REPO_ROOT / "common/test"] ...@@ -588,3 +588,5 @@ COMPREHENSIVE_THEME_DIRS = [REPO_ROOT / "themes", REPO_ROOT / "common/test"]
COMPREHENSIVE_THEME_LOCALE_PATHS = [REPO_ROOT / "themes/conf/locale", ] COMPREHENSIVE_THEME_LOCALE_PATHS = [REPO_ROOT / "themes/conf/locale", ]
LMS_ROOT_URL = "http://localhost:8000" LMS_ROOT_URL = "http://localhost:8000"
ECOMMERCE_API_URL = 'https://ecommerce.example.com/api/v2/'
...@@ -4,13 +4,14 @@ from edx_rest_api_client.client import EdxRestApiClient ...@@ -4,13 +4,14 @@ from edx_rest_api_client.client import EdxRestApiClient
from eventtracking import tracker from eventtracking import tracker
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
ECOMMERCE_DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ" ECOMMERCE_DATE_FORMAT = '%Y-%m-%dT%H:%M:%SZ'
def create_tracking_context(user): def create_tracking_context(user):
""" Assembles attributes from user and request objects to be sent along """ Assembles attributes from user and request objects to be sent along
in ecommerce api calls for tracking purposes. """ in E-Commerce API calls for tracking purposes. """
context_tracker = tracker.get_tracker().resolve_context() context_tracker = tracker.get_tracker().resolve_context()
return { return {
...@@ -22,27 +23,19 @@ def create_tracking_context(user): ...@@ -22,27 +23,19 @@ def create_tracking_context(user):
def is_commerce_service_configured(): def is_commerce_service_configured():
""" """
Return a Boolean indicating whether or not configuration is present to use Return a Boolean indicating whether or not configuration is present to use the external commerce service.
the external commerce service.
""" """
ecommerce_api_url = configuration_helpers.get_value("ECOMMERCE_API_URL", settings.ECOMMERCE_API_URL) ecommerce_api_url = configuration_helpers.get_value('ECOMMERCE_API_URL', settings.ECOMMERCE_API_URL)
ecommerce_api_signing_key = configuration_helpers.get_value( return bool(ecommerce_api_url)
"ECOMMERCE_API_SIGNING_KEY", settings.ECOMMERCE_API_SIGNING_KEY,
)
return bool(ecommerce_api_url and ecommerce_api_signing_key)
def ecommerce_api_client(user, session=None): def ecommerce_api_client(user, session=None, token_expiration=None):
""" Returns an E-Commerce API client setup with authentication for the specified user. """ """ Returns an E-Commerce API client setup with authentication for the specified user. """
jwt_auth = configuration_helpers.get_value("JWT_AUTH", settings.JWT_AUTH) claims = {'tracking_context': create_tracking_context(user)}
jwt = JwtBuilder(user).build_token(['email', 'profile'], expires_in=token_expiration, additional_claims=claims)
return EdxRestApiClient( return EdxRestApiClient(
configuration_helpers.get_value("ECOMMERCE_API_URL", settings.ECOMMERCE_API_URL), configuration_helpers.get_value('ECOMMERCE_API_URL', settings.ECOMMERCE_API_URL),
configuration_helpers.get_value("ECOMMERCE_API_SIGNING_KEY", settings.ECOMMERCE_API_SIGNING_KEY), jwt=jwt,
user.username,
user.profile.name if hasattr(user, 'profile') else None,
user.email,
tracking_context=create_tracking_context(user),
issuer=jwt_auth['JWT_ISSUER'],
expires_in=jwt_auth['JWT_EXPIRATION'],
session=session session=session
) )
...@@ -11,7 +11,7 @@ from django.test.utils import override_settings ...@@ -11,7 +11,7 @@ from django.test.utils import override_settings
from django.db import connection from django.db import connection
from nose.plugins.attrib import attr from nose.plugins.attrib import attr
import httpretty import httpretty
from lms.djangoapps.commerce.tests import TEST_API_SIGNING_KEY, TEST_API_URL from lms.djangoapps.commerce.tests import TEST_API_URL
import mock import mock
import pytz import pytz
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
...@@ -575,7 +575,6 @@ class CreditRequirementApiTests(CreditApiTestBase): ...@@ -575,7 +575,6 @@ class CreditRequirementApiTests(CreditApiTestBase):
@httpretty.activate @httpretty.activate
@override_settings( @override_settings(
ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_URL=TEST_API_URL,
ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY,
ECOMMERCE_SERVICE_WORKER_USERNAME=TEST_ECOMMERCE_WORKER ECOMMERCE_SERVICE_WORKER_USERNAME=TEST_ECOMMERCE_WORKER
) )
def test_satisfy_all_requirements(self): def test_satisfy_all_requirements(self):
...@@ -622,7 +621,7 @@ class CreditRequirementApiTests(CreditApiTestBase): ...@@ -622,7 +621,7 @@ class CreditRequirementApiTests(CreditApiTestBase):
self.assertFalse(api.is_user_eligible_for_credit(user.username, self.course_key)) self.assertFalse(api.is_user_eligible_for_credit(user.username, self.course_key))
# Satisfy the other requirement # Satisfy the other requirement
with self.assertNumQueries(21): with self.assertNumQueries(25):
api.set_credit_requirement_status( api.set_credit_requirement_status(
user, user,
self.course_key, self.course_key,
...@@ -676,7 +675,7 @@ class CreditRequirementApiTests(CreditApiTestBase): ...@@ -676,7 +675,7 @@ class CreditRequirementApiTests(CreditApiTestBase):
# Delete the eligibility entries and satisfy the user's eligibility # Delete the eligibility entries and satisfy the user's eligibility
# requirement again to trigger eligibility notification # requirement again to trigger eligibility notification
CreditEligibility.objects.all().delete() CreditEligibility.objects.all().delete()
with self.assertNumQueries(16): with self.assertNumQueries(17):
api.set_credit_requirement_status( api.set_credit_requirement_status(
user, user,
self.course_key, self.course_key,
...@@ -1167,7 +1166,6 @@ class CreditProviderIntegrationApiTests(CreditApiTestBase): ...@@ -1167,7 +1166,6 @@ class CreditProviderIntegrationApiTests(CreditApiTestBase):
@skip_unless_lms @skip_unless_lms
@override_settings( @override_settings(
ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_URL=TEST_API_URL,
ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY,
ECOMMERCE_SERVICE_WORKER_USERNAME=TEST_ECOMMERCE_WORKER ECOMMERCE_SERVICE_WORKER_USERNAME=TEST_ECOMMERCE_WORKER
) )
@ddt.ddt @ddt.ddt
......
...@@ -19,7 +19,6 @@ from student.tests.factories import UserFactory ...@@ -19,7 +19,6 @@ from student.tests.factories import UserFactory
UTILITY_MODULE = 'openedx.core.lib.edx_api_utils' UTILITY_MODULE = 'openedx.core.lib.edx_api_utils'
TEST_API_URL = 'http://www-internal.example.com/api' TEST_API_URL = 'http://www-internal.example.com/api'
TEST_API_SIGNING_KEY = 'edx'
@skip_unless_lms @skip_unless_lms
...@@ -200,8 +199,7 @@ class TestGetEdxApiData(ProgramsApiConfigMixin, CacheIsolationTestCase): ...@@ -200,8 +199,7 @@ class TestGetEdxApiData(ProgramsApiConfigMixin, CacheIsolationTestCase):
self.assertTrue(mock_exception.called) self.assertTrue(mock_exception.called)
self.assertEqual(actual, []) self.assertEqual(actual, [])
@override_settings(JWT_AUTH={'JWT_ISSUER': 'http://example.com/oauth', 'JWT_EXPIRATION': 30}, @override_settings(ECOMMERCE_API_URL=TEST_API_URL)
ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY, ECOMMERCE_API_URL=TEST_API_URL)
def test_client_passed(self): def test_client_passed(self):
""" Verify that when API client is passed edx_rest_api_client is not """ Verify that when API client is passed edx_rest_api_client is not
used. used.
......
...@@ -33,17 +33,22 @@ class JwtBuilder(object): ...@@ -33,17 +33,22 @@ class JwtBuilder(object):
self.secret = secret self.secret = secret
self.jwt_auth = configuration_helpers.get_value('JWT_AUTH', settings.JWT_AUTH) self.jwt_auth = configuration_helpers.get_value('JWT_AUTH', settings.JWT_AUTH)
def build_token(self, scopes, expires_in, aud=None): def build_token(self, scopes, expires_in=None, aud=None, additional_claims=None):
"""Returns a JWT access token. """Returns a JWT access token.
Arguments: Arguments:
scopes (list): Scopes controlling which optional claims are included in the token. scopes (list): Scopes controlling which optional claims are included in the token.
expires_in (int): Time to token expiry, specified in seconds.
Keyword Arguments: Keyword Arguments:
expires_in (int): Time to token expiry, specified in seconds.
aud (string): Overrides configured JWT audience claim. aud (string): Overrides configured JWT audience claim.
additional_claims (dict): Additional claims to include in the token.
Returns:
str: Encoded JWT
""" """
now = int(time()) now = int(time())
expires_in = expires_in or self.jwt_auth['JWT_EXPIRATION']
payload = { payload = {
'aud': aud if aud else self.jwt_auth['JWT_AUDIENCE'], 'aud': aud if aud else self.jwt_auth['JWT_AUDIENCE'],
'exp': now + expires_in, 'exp': now + expires_in,
...@@ -54,6 +59,9 @@ class JwtBuilder(object): ...@@ -54,6 +59,9 @@ class JwtBuilder(object):
'sub': anonymous_id_for_user(self.user, None), 'sub': anonymous_id_for_user(self.user, None),
} }
if additional_claims:
payload.update(additional_claims)
for scope in scopes: for scope in scopes:
handler = self.claim_handlers.get(scope) handler = self.claim_handlers.get(scope)
......
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