Skip to content

Commit

Permalink
Fixed #84: Ensure not possible to skip steps by manipulating URL
Browse files Browse the repository at this point in the history
  • Loading branch information
stephenmuss committed Jun 13, 2012
1 parent 3983ec8 commit c65ee0e
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 1 deletion.
3 changes: 2 additions & 1 deletion shop/shipping/backends/flat_rate.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.shortcuts import render_to_response
from django.template import RequestContext

from shop.util.decorators import on_method, shop_login_required
from shop.util.decorators import on_method, shop_login_required, order_required


class FlatRateShipping(object):
Expand All @@ -23,6 +23,7 @@ def __init__(self, shop):
self.rate = getattr(settings, 'SHOP_SHIPPING_FLAT_RATE', '10')

@on_method(shop_login_required)
@on_method(order_required)
def view_process_order(self, request):
"""
A simple (not class-based) view to process an order.
Expand Down
23 changes: 23 additions & 0 deletions shop/tests/shipping.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from shop.backends_pool import backends_pool
from shop.models.ordermodel import Order
from shop.shipping.backends.flat_rate import FlatRateShipping
from shop.shipping.api import ShippingAPI
from shop.tests.util import Mock
from shop.tests.utils.context_managers import SettingsOverride
Expand Down Expand Up @@ -156,6 +157,11 @@ def test_adding_shipping_costs_twice_works(self):

class FlatRateShippingTestCase(TestCase):
"""Tests for ``shop.shipping.backends.flat_rate.FlatRateShipping``."""
def setUp(self):
self.backend = FlatRateShipping(shop=ShippingAPI())
self.user = User.objects.create(username="test", email="test@example.com")
self.request = Mock()
setattr(self.request, 'user', self.user)

def test_must_be_logged_in_if_setting_is_true(self):
with SettingsOverride(SHOP_FORCE_LOGIN=True):
Expand All @@ -165,3 +171,20 @@ def test_must_be_logged_in_if_setting_is_true(self):
resp = self.client.get(reverse('flat_process'))
self.assertEqual(resp.status_code, 302)
self.assertTrue('accounts/login/' in resp._headers['location'][1])

def test_order_required_before_shipping_processed(self):
""" See issue #84 """
# Session only (no order)
response = self.client.get(reverse('flat_process'))
self.assertEqual(response.status_code, 302)

# User logged in (no order)
view = self.backend.view_process_order(self.request)
self.assertEqual(view.get('location'), '/')

# User logged in with order
order = Order()
setattr(order, 'user', self.user)
order.save()
view = self.backend.view_process_order(self.request)
self.assertEqual(view.get('location'), reverse('checkout_payment'))
34 changes: 34 additions & 0 deletions shop/util/decorators.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
"""Decorators for the django-shop application."""
from functools import wraps

from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import user_passes_test
from django.http import HttpResponseRedirect

from shop.util.login_mixin import get_test_func
from shop.util.order import get_order_from_request


def on_method(function_decorator):
Expand Down Expand Up @@ -37,3 +41,33 @@ def shop_login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME,
if function:
return actual_decorator(function)
return actual_decorator


def order_required(redirect_url='/'):
"""
Ensures that an order exists before carrying out any additional functions
that rely on one.
If an order does not exist the browser will be redirected to another page
supplied in the optional keyword argument `redirect_url`.
Usage:
@order_required
def some_view(...
OR:
@order_required(redirect_url='/some/path/')
def some_view(...
"""
if callable(redirect_url):
func = redirect_url
decorator = order_required()
return decorator(func)

def decorator(func):
def inner(request, *args, **kwargs):
if get_order_from_request(request) is None:
return HttpResponseRedirect(redirect_url)
return func(request, *args, **kwargs)
return wraps(func)(inner)
return decorator

0 comments on commit c65ee0e

Please sign in to comment.