Commit 81c1ae35 by chrisndodge

Merge pull request #5903 from edx/cdodge/shoppingcart-isolation

harden down access to the shoppingcart if the ENABLE_PAID_COURSE_REGISTR...
parents bd87a9dd 58b4982d
......@@ -265,6 +265,7 @@ class DashboardTest(ModuleStoreTestCase):
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@patch('courseware.views.log.warning')
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_PAID_COURSE_REGISTRATION': True})
def test_blocked_course_scenario(self, log_warning):
self.client.login(username="jack", password="test")
......
......@@ -49,6 +49,7 @@ from xmodule.tabs import CourseTabList, StaffGradingTab, PeerGradingTab, OpenEnd
from xmodule.x_module import STUDENT_VIEW
import shoppingcart
from shoppingcart.models import CourseRegistrationCode
from shoppingcart.utils import is_shopping_cart_enabled
from opaque_keys import InvalidKeyError
from microsite_configuration import microsite
......@@ -731,8 +732,8 @@ def course_about(request, course_id):
registration_price = 0
in_cart = False
reg_then_add_to_cart_link = ""
if (settings.FEATURES.get('ENABLE_SHOPPING_CART') and
settings.FEATURES.get('ENABLE_PAID_COURSE_REGISTRATION')):
if (is_shopping_cart_enabled()):
registration_price = CourseMode.min_course_price_for_currency(course_key,
settings.PAID_COURSE_REGISTRATION_CURRENCY[0])
if request.user.is_authenticated():
......
......@@ -1604,6 +1604,7 @@ class TestInstructorAPILevelsAccess(ModuleStoreTestCase, LoginEnrollmentTestCase
@ddt.ddt
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_PAID_COURSE_REGISTRATION': True})
class TestInstructorAPILevelsDataDump(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""
Test endpoints that show data without side effects.
......
......@@ -5,9 +5,8 @@ navigation. We want to do this in the context_processor to
1) keep database accesses out of templates (this led to a transaction bug with user email changes)
2) because navigation.html is "called" by being included in other templates, there's no "views.py" to put this.
"""
from django.conf import settings
import shoppingcart
from microsite_configuration import microsite
def user_has_cart_context_processor(request):
......@@ -19,16 +18,8 @@ def user_has_cart_context_processor(request):
display_shopping_cart = (
# user is logged in and
request.user.is_authenticated() and
# settings enable paid course reg
microsite.get_value(
'ENABLE_PAID_COURSE_REGISTRATION',
settings.FEATURES.get('ENABLE_PAID_COURSE_REGISTRATION')
) and
# settings enable shopping cart
microsite.get_value(
'ENABLE_SHOPPING_CART',
settings.FEATURES.get('ENABLE_SHOPPING_CART')
) and
# do we have the feature turned on
shoppingcart.utils.is_shopping_cart_enabled() and
# user's cart has PaidCourseRegistrations
shoppingcart.models.Order.user_cart_has_items(
request.user,
......
"""
This file defines any decorators used by the shopping cart app
"""
from django.http import Http404
from .utils import is_shopping_cart_enabled
def enforce_shopping_cart_enabled(func):
"""
Is a decorator that forces a wrapped method to be run in a runtime
which has the ENABLE_SHOPPING_CART flag set
"""
def func_wrapper(*args, **kwargs):
"""
Wrapper function that does the enforcement that
the shopping cart feature is enabled
"""
if not is_shopping_cart_enabled():
raise Http404
return func(*args, **kwargs)
return func_wrapper
......@@ -64,6 +64,7 @@ MODULESTORE_CONFIG = mixed_store_config(settings.COMMON_TEST_DATA_ROOT, {}, incl
@override_settings(MODULESTORE=MODULESTORE_CONFIG)
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_PAID_COURSE_REGISTRATION': True})
class ShoppingCartViewsTests(ModuleStoreTestCase):
def setUp(self):
patcher = patch('student.models.tracker')
......@@ -963,8 +964,36 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
((template, _context), _tmp) = render_mock.call_args
self.assertEqual(template, cert_item.single_item_receipt_template)
def _assert_404(self, url, use_post=False):
"""
Helper method to assert that a given url will return a 404 status code
"""
if use_post:
response = self.client.post(url)
else:
response = self.client.get(url)
self.assertEquals(response.status_code, 404)
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_PAID_COURSE_REGISTRATION': False})
def test_disabled_paid_courses(self):
"""
Assert that the pages that require ENABLE_PAID_COURSE_REGISTRATION=True return a
HTTP 404 status code when we have this flag turned off
"""
self.login_user()
self._assert_404(reverse('shoppingcart.views.show_cart', args=[]))
self._assert_404(reverse('shoppingcart.views.clear_cart', args=[]))
self._assert_404(reverse('shoppingcart.views.remove_item', args=[]), use_post=True)
self._assert_404(reverse('shoppingcart.views.register_code_redemption', args=["testing"]))
self._assert_404(reverse('shoppingcart.views.use_code', args=[]), use_post=True)
self._assert_404(reverse('shoppingcart.views.update_user_cart', args=[]))
self._assert_404(reverse('shoppingcart.views.reset_code_redemption', args=[]), use_post=True)
self._assert_404(reverse('shoppingcart.views.billing_details', args=[]))
self._assert_404(reverse('shoppingcart.views.register_courses', args=[]))
@override_settings(MODULESTORE=MODULESTORE_CONFIG)
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_PAID_COURSE_REGISTRATION': True})
class RegistrationCodeRedemptionCourseEnrollment(ModuleStoreTestCase):
"""
Test suite for RegistrationCodeRedemption Course Enrollments
......
......@@ -6,23 +6,19 @@ urlpatterns = patterns('shoppingcart.views', # nopep8
url(r'^receipt/(?P<ordernum>[0-9]*)/$', 'show_receipt'),
url(r'^donation/$', 'donate', name='donation'),
url(r'^csv_report/$', 'csv_report', name='payment_csv_report'),
# These following URLs are only valid if the ENABLE_SHOPPING_CART feature flag is set
url(r'^$', 'show_cart'),
url(r'^clear/$', 'clear_cart'),
url(r'^remove_item/$', 'remove_item'),
url(r'^add/course/{}/$'.format(settings.COURSE_ID_PATTERN), 'add_course_to_cart', name='add_course_to_cart'),
url(r'^register/redeem/(?P<registration_code>[0-9A-Za-z]+)/$', 'register_code_redemption', name='register_code_redemption'),
url(r'^use_code/$', 'use_code'),
url(r'^update_user_cart/$', 'update_user_cart'),
url(r'^reset_code_redemption/$', 'reset_code_redemption'),
url(r'^billing_details/$', 'billing_details', name='billing_details'),
url(r'^register_courses/$', 'register_courses'),
)
if settings.FEATURES['ENABLE_SHOPPING_CART']:
urlpatterns += patterns(
'shoppingcart.views',
url(r'^$', 'show_cart'),
url(r'^clear/$', 'clear_cart'),
url(r'^remove_item/$', 'remove_item'),
url(r'^add/course/{}/$'.format(settings.COURSE_ID_PATTERN), 'add_course_to_cart', name='add_course_to_cart'),
url(r'^register/redeem/(?P<registration_code>[0-9A-Za-z]+)/$', 'register_code_redemption', name='register_code_redemption'),
url(r'^use_code/$', 'use_code'),
url(r'^update_user_cart/$', 'update_user_cart'),
url(r'^reset_code_redemption/$', 'reset_code_redemption'),
url(r'^billing_details/$', 'billing_details', name='billing_details'),
url(r'^register_courses/$', 'register_courses'),
)
if settings.FEATURES.get('ENABLE_PAYMENT_FAKE'):
from shoppingcart.tests.payment_fake import PaymentFakeView
urlpatterns += patterns(
......
"""
Utility methods for the Shopping Cart app
"""
from django.conf import settings
from microsite_configuration import microsite
def is_shopping_cart_enabled():
"""
Utility method to check the various configuration to verify that
all of the settings have been enabled
"""
enable_paid_course_registration = microsite.get_value(
'ENABLE_PAID_COURSE_REGISTRATION',
settings.FEATURES.get('ENABLE_PAID_COURSE_REGISTRATION')
)
enable_shopping_cart = microsite.get_value(
'ENABLE_SHOPPING_CART',
settings.FEATURES.get('ENABLE_SHOPPING_CART')
)
return (enable_paid_course_registration and enable_shopping_cart)
......@@ -33,7 +33,7 @@ from .exceptions import (
)
from .models import (
Order, OrderTypes,
PaidCourseRegistration, OrderItem, Coupon, CourseRegCodeItem,
PaidCourseRegistration, OrderItem, Coupon,
CouponRedemption, CourseRegistrationCode, RegistrationCodeRedemption,
Donation, DonationConfiguration
)
......@@ -44,6 +44,7 @@ from .processors import (
import json
from xmodule_django.models import CourseKeyField
from .decorators import enforce_shopping_cart_enabled
log = logging.getLogger("shoppingcart")
AUDIT_LOG = logging.getLogger("audit")
......@@ -94,6 +95,7 @@ def add_course_to_cart(request, course_id):
@login_required
@enforce_shopping_cart_enabled
def update_user_cart(request):
"""
when user change the number-of-students from the UI then
......@@ -127,6 +129,7 @@ def update_user_cart(request):
@login_required
@enforce_shopping_cart_enabled
def show_cart(request):
"""
This view shows cart items.
......@@ -158,6 +161,7 @@ def show_cart(request):
@login_required
@enforce_shopping_cart_enabled
def clear_cart(request):
cart = Order.get_cart_for_user(request.user)
cart.clear()
......@@ -175,6 +179,7 @@ def clear_cart(request):
@login_required
@enforce_shopping_cart_enabled
def remove_item(request):
"""
This will remove an item from the user cart and also delete the corresponding coupon codes redemption.
......@@ -227,6 +232,7 @@ def remove_code_redemption(order_item_course_id, item_id, item, user):
@login_required
@enforce_shopping_cart_enabled
def reset_code_redemption(request):
"""
This method reset the code redemption from user cart items.
......@@ -239,6 +245,7 @@ def reset_code_redemption(request):
@login_required
@enforce_shopping_cart_enabled
def use_code(request):
"""
This method may generate the discount against valid coupon code
......@@ -291,6 +298,7 @@ def get_reg_code_validity(registration_code, request, limiter):
@require_http_methods(["GET", "POST"])
@login_required
@enforce_shopping_cart_enabled
def register_code_redemption(request, registration_code):
"""
This view allows the student to redeem the registration code
......@@ -382,6 +390,7 @@ def use_coupon_code(coupons, user):
@login_required
@enforce_shopping_cart_enabled
def register_courses(request):
"""
This method enroll the user for available course(s)
......@@ -518,6 +527,7 @@ def postpay_callback(request):
@require_http_methods(["GET", "POST"])
@login_required
@enforce_shopping_cart_enabled
def billing_details(request):
"""
This is the view for capturing additional billing details
......
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