1. from functools import update_wrapper, wraps
    
  2. from unittest import TestCase, mock
    
  3. 
    
  4. from django.contrib.admin.views.decorators import staff_member_required
    
  5. from django.contrib.auth.decorators import (
    
  6.     login_required,
    
  7.     permission_required,
    
  8.     user_passes_test,
    
  9. )
    
  10. from django.http import HttpRequest, HttpResponse, HttpResponseNotAllowed
    
  11. from django.middleware.clickjacking import XFrameOptionsMiddleware
    
  12. from django.test import SimpleTestCase
    
  13. from django.utils.decorators import method_decorator
    
  14. from django.utils.functional import keep_lazy, keep_lazy_text, lazy
    
  15. from django.utils.safestring import mark_safe
    
  16. from django.views.decorators.cache import cache_control, cache_page, never_cache
    
  17. from django.views.decorators.clickjacking import (
    
  18.     xframe_options_deny,
    
  19.     xframe_options_exempt,
    
  20.     xframe_options_sameorigin,
    
  21. )
    
  22. from django.views.decorators.http import (
    
  23.     condition,
    
  24.     require_GET,
    
  25.     require_http_methods,
    
  26.     require_POST,
    
  27.     require_safe,
    
  28. )
    
  29. from django.views.decorators.vary import vary_on_cookie, vary_on_headers
    
  30. 
    
  31. 
    
  32. def fully_decorated(request):
    
  33.     """Expected __doc__"""
    
  34.     return HttpResponse("<html><body>dummy</body></html>")
    
  35. 
    
  36. 
    
  37. fully_decorated.anything = "Expected __dict__"
    
  38. 
    
  39. 
    
  40. def compose(*functions):
    
  41.     # compose(f, g)(*args, **kwargs) == f(g(*args, **kwargs))
    
  42.     functions = list(reversed(functions))
    
  43. 
    
  44.     def _inner(*args, **kwargs):
    
  45.         result = functions[0](*args, **kwargs)
    
  46.         for f in functions[1:]:
    
  47.             result = f(result)
    
  48.         return result
    
  49. 
    
  50.     return _inner
    
  51. 
    
  52. 
    
  53. full_decorator = compose(
    
  54.     # django.views.decorators.http
    
  55.     require_http_methods(["GET"]),
    
  56.     require_GET,
    
  57.     require_POST,
    
  58.     require_safe,
    
  59.     condition(lambda r: None, lambda r: None),
    
  60.     # django.views.decorators.vary
    
  61.     vary_on_headers("Accept-language"),
    
  62.     vary_on_cookie,
    
  63.     # django.views.decorators.cache
    
  64.     cache_page(60 * 15),
    
  65.     cache_control(private=True),
    
  66.     never_cache,
    
  67.     # django.contrib.auth.decorators
    
  68.     # Apply user_passes_test twice to check #9474
    
  69.     user_passes_test(lambda u: True),
    
  70.     login_required,
    
  71.     permission_required("change_world"),
    
  72.     # django.contrib.admin.views.decorators
    
  73.     staff_member_required,
    
  74.     # django.utils.functional
    
  75.     keep_lazy(HttpResponse),
    
  76.     keep_lazy_text,
    
  77.     lazy,
    
  78.     # django.utils.safestring
    
  79.     mark_safe,
    
  80. )
    
  81. 
    
  82. fully_decorated = full_decorator(fully_decorated)
    
  83. 
    
  84. 
    
  85. class DecoratorsTest(TestCase):
    
  86.     def test_attributes(self):
    
  87.         """
    
  88.         Built-in decorators set certain attributes of the wrapped function.
    
  89.         """
    
  90.         self.assertEqual(fully_decorated.__name__, "fully_decorated")
    
  91.         self.assertEqual(fully_decorated.__doc__, "Expected __doc__")
    
  92.         self.assertEqual(fully_decorated.__dict__["anything"], "Expected __dict__")
    
  93. 
    
  94.     def test_user_passes_test_composition(self):
    
  95.         """
    
  96.         The user_passes_test decorator can be applied multiple times (#9474).
    
  97.         """
    
  98. 
    
  99.         def test1(user):
    
  100.             user.decorators_applied.append("test1")
    
  101.             return True
    
  102. 
    
  103.         def test2(user):
    
  104.             user.decorators_applied.append("test2")
    
  105.             return True
    
  106. 
    
  107.         def callback(request):
    
  108.             return request.user.decorators_applied
    
  109. 
    
  110.         callback = user_passes_test(test1)(callback)
    
  111.         callback = user_passes_test(test2)(callback)
    
  112. 
    
  113.         class DummyUser:
    
  114.             pass
    
  115. 
    
  116.         class DummyRequest:
    
  117.             pass
    
  118. 
    
  119.         request = DummyRequest()
    
  120.         request.user = DummyUser()
    
  121.         request.user.decorators_applied = []
    
  122.         response = callback(request)
    
  123. 
    
  124.         self.assertEqual(response, ["test2", "test1"])
    
  125. 
    
  126.     def test_cache_page(self):
    
  127.         def my_view(request):
    
  128.             return "response"
    
  129. 
    
  130.         my_view_cached = cache_page(123)(my_view)
    
  131.         self.assertEqual(my_view_cached(HttpRequest()), "response")
    
  132.         my_view_cached2 = cache_page(123, key_prefix="test")(my_view)
    
  133.         self.assertEqual(my_view_cached2(HttpRequest()), "response")
    
  134. 
    
  135.     def test_require_safe_accepts_only_safe_methods(self):
    
  136.         """
    
  137.         Test for the require_safe decorator.
    
  138.         A view returns either a response or an exception.
    
  139.         Refs #15637.
    
  140.         """
    
  141. 
    
  142.         def my_view(request):
    
  143.             return HttpResponse("OK")
    
  144. 
    
  145.         my_safe_view = require_safe(my_view)
    
  146.         request = HttpRequest()
    
  147.         request.method = "GET"
    
  148.         self.assertIsInstance(my_safe_view(request), HttpResponse)
    
  149.         request.method = "HEAD"
    
  150.         self.assertIsInstance(my_safe_view(request), HttpResponse)
    
  151.         request.method = "POST"
    
  152.         self.assertIsInstance(my_safe_view(request), HttpResponseNotAllowed)
    
  153.         request.method = "PUT"
    
  154.         self.assertIsInstance(my_safe_view(request), HttpResponseNotAllowed)
    
  155.         request.method = "DELETE"
    
  156.         self.assertIsInstance(my_safe_view(request), HttpResponseNotAllowed)
    
  157. 
    
  158. 
    
  159. # For testing method_decorator, a decorator that assumes a single argument.
    
  160. # We will get type arguments if there is a mismatch in the number of arguments.
    
  161. def simple_dec(func):
    
  162.     def wrapper(arg):
    
  163.         return func("test:" + arg)
    
  164. 
    
  165.     return wraps(func)(wrapper)
    
  166. 
    
  167. 
    
  168. simple_dec_m = method_decorator(simple_dec)
    
  169. 
    
  170. 
    
  171. # For testing method_decorator, two decorators that add an attribute to the function
    
  172. def myattr_dec(func):
    
  173.     def wrapper(*args, **kwargs):
    
  174.         return func(*args, **kwargs)
    
  175. 
    
  176.     wrapper.myattr = True
    
  177.     return wrapper
    
  178. 
    
  179. 
    
  180. myattr_dec_m = method_decorator(myattr_dec)
    
  181. 
    
  182. 
    
  183. def myattr2_dec(func):
    
  184.     def wrapper(*args, **kwargs):
    
  185.         return func(*args, **kwargs)
    
  186. 
    
  187.     wrapper.myattr2 = True
    
  188.     return wrapper
    
  189. 
    
  190. 
    
  191. myattr2_dec_m = method_decorator(myattr2_dec)
    
  192. 
    
  193. 
    
  194. class ClsDec:
    
  195.     def __init__(self, myattr):
    
  196.         self.myattr = myattr
    
  197. 
    
  198.     def __call__(self, f):
    
  199.         def wrapped():
    
  200.             return f() and self.myattr
    
  201. 
    
  202.         return update_wrapper(wrapped, f)
    
  203. 
    
  204. 
    
  205. class MethodDecoratorTests(SimpleTestCase):
    
  206.     """
    
  207.     Tests for method_decorator
    
  208.     """
    
  209. 
    
  210.     def test_preserve_signature(self):
    
  211.         class Test:
    
  212.             @simple_dec_m
    
  213.             def say(self, arg):
    
  214.                 return arg
    
  215. 
    
  216.         self.assertEqual("test:hello", Test().say("hello"))
    
  217. 
    
  218.     def test_preserve_attributes(self):
    
  219.         # Sanity check myattr_dec and myattr2_dec
    
  220.         @myattr_dec
    
  221.         def func():
    
  222.             pass
    
  223. 
    
  224.         self.assertIs(getattr(func, "myattr", False), True)
    
  225. 
    
  226.         @myattr2_dec
    
  227.         def func():
    
  228.             pass
    
  229. 
    
  230.         self.assertIs(getattr(func, "myattr2", False), True)
    
  231. 
    
  232.         @myattr_dec
    
  233.         @myattr2_dec
    
  234.         def func():
    
  235.             pass
    
  236. 
    
  237.         self.assertIs(getattr(func, "myattr", False), True)
    
  238.         self.assertIs(getattr(func, "myattr2", False), False)
    
  239. 
    
  240.         # Decorate using method_decorator() on the method.
    
  241.         class TestPlain:
    
  242.             @myattr_dec_m
    
  243.             @myattr2_dec_m
    
  244.             def method(self):
    
  245.                 "A method"
    
  246.                 pass
    
  247. 
    
  248.         # Decorate using method_decorator() on both the class and the method.
    
  249.         # The decorators applied to the methods are applied before the ones
    
  250.         # applied to the class.
    
  251.         @method_decorator(myattr_dec_m, "method")
    
  252.         class TestMethodAndClass:
    
  253.             @method_decorator(myattr2_dec_m)
    
  254.             def method(self):
    
  255.                 "A method"
    
  256.                 pass
    
  257. 
    
  258.         # Decorate using an iterable of function decorators.
    
  259.         @method_decorator((myattr_dec, myattr2_dec), "method")
    
  260.         class TestFunctionIterable:
    
  261.             def method(self):
    
  262.                 "A method"
    
  263.                 pass
    
  264. 
    
  265.         # Decorate using an iterable of method decorators.
    
  266.         decorators = (myattr_dec_m, myattr2_dec_m)
    
  267. 
    
  268.         @method_decorator(decorators, "method")
    
  269.         class TestMethodIterable:
    
  270.             def method(self):
    
  271.                 "A method"
    
  272.                 pass
    
  273. 
    
  274.         tests = (
    
  275.             TestPlain,
    
  276.             TestMethodAndClass,
    
  277.             TestFunctionIterable,
    
  278.             TestMethodIterable,
    
  279.         )
    
  280.         for Test in tests:
    
  281.             with self.subTest(Test=Test):
    
  282.                 self.assertIs(getattr(Test().method, "myattr", False), True)
    
  283.                 self.assertIs(getattr(Test().method, "myattr2", False), True)
    
  284.                 self.assertIs(getattr(Test.method, "myattr", False), True)
    
  285.                 self.assertIs(getattr(Test.method, "myattr2", False), True)
    
  286.                 self.assertEqual(Test.method.__doc__, "A method")
    
  287.                 self.assertEqual(Test.method.__name__, "method")
    
  288. 
    
  289.     def test_new_attribute(self):
    
  290.         """A decorator that sets a new attribute on the method."""
    
  291. 
    
  292.         def decorate(func):
    
  293.             func.x = 1
    
  294.             return func
    
  295. 
    
  296.         class MyClass:
    
  297.             @method_decorator(decorate)
    
  298.             def method(self):
    
  299.                 return True
    
  300. 
    
  301.         obj = MyClass()
    
  302.         self.assertEqual(obj.method.x, 1)
    
  303.         self.assertIs(obj.method(), True)
    
  304. 
    
  305.     def test_bad_iterable(self):
    
  306.         decorators = {myattr_dec_m, myattr2_dec_m}
    
  307.         msg = "'set' object is not subscriptable"
    
  308.         with self.assertRaisesMessage(TypeError, msg):
    
  309. 
    
  310.             @method_decorator(decorators, "method")
    
  311.             class TestIterable:
    
  312.                 def method(self):
    
  313.                     "A method"
    
  314.                     pass
    
  315. 
    
  316.     # Test for argumented decorator
    
  317.     def test_argumented(self):
    
  318.         class Test:
    
  319.             @method_decorator(ClsDec(False))
    
  320.             def method(self):
    
  321.                 return True
    
  322. 
    
  323.         self.assertIs(Test().method(), False)
    
  324. 
    
  325.     def test_descriptors(self):
    
  326.         def original_dec(wrapped):
    
  327.             def _wrapped(arg):
    
  328.                 return wrapped(arg)
    
  329. 
    
  330.             return _wrapped
    
  331. 
    
  332.         method_dec = method_decorator(original_dec)
    
  333. 
    
  334.         class bound_wrapper:
    
  335.             def __init__(self, wrapped):
    
  336.                 self.wrapped = wrapped
    
  337.                 self.__name__ = wrapped.__name__
    
  338. 
    
  339.             def __call__(self, arg):
    
  340.                 return self.wrapped(arg)
    
  341. 
    
  342.             def __get__(self, instance, cls=None):
    
  343.                 return self
    
  344. 
    
  345.         class descriptor_wrapper:
    
  346.             def __init__(self, wrapped):
    
  347.                 self.wrapped = wrapped
    
  348.                 self.__name__ = wrapped.__name__
    
  349. 
    
  350.             def __get__(self, instance, cls=None):
    
  351.                 return bound_wrapper(self.wrapped.__get__(instance, cls))
    
  352. 
    
  353.         class Test:
    
  354.             @method_dec
    
  355.             @descriptor_wrapper
    
  356.             def method(self, arg):
    
  357.                 return arg
    
  358. 
    
  359.         self.assertEqual(Test().method(1), 1)
    
  360. 
    
  361.     def test_class_decoration(self):
    
  362.         """
    
  363.         @method_decorator can be used to decorate a class and its methods.
    
  364.         """
    
  365. 
    
  366.         def deco(func):
    
  367.             def _wrapper(*args, **kwargs):
    
  368.                 return True
    
  369. 
    
  370.             return _wrapper
    
  371. 
    
  372.         @method_decorator(deco, name="method")
    
  373.         class Test:
    
  374.             def method(self):
    
  375.                 return False
    
  376. 
    
  377.         self.assertTrue(Test().method())
    
  378. 
    
  379.     def test_tuple_of_decorators(self):
    
  380.         """
    
  381.         @method_decorator can accept a tuple of decorators.
    
  382.         """
    
  383. 
    
  384.         def add_question_mark(func):
    
  385.             def _wrapper(*args, **kwargs):
    
  386.                 return func(*args, **kwargs) + "?"
    
  387. 
    
  388.             return _wrapper
    
  389. 
    
  390.         def add_exclamation_mark(func):
    
  391.             def _wrapper(*args, **kwargs):
    
  392.                 return func(*args, **kwargs) + "!"
    
  393. 
    
  394.             return _wrapper
    
  395. 
    
  396.         # The order should be consistent with the usual order in which
    
  397.         # decorators are applied, e.g.
    
  398.         #    @add_exclamation_mark
    
  399.         #    @add_question_mark
    
  400.         #    def func():
    
  401.         #        ...
    
  402.         decorators = (add_exclamation_mark, add_question_mark)
    
  403. 
    
  404.         @method_decorator(decorators, name="method")
    
  405.         class TestFirst:
    
  406.             def method(self):
    
  407.                 return "hello world"
    
  408. 
    
  409.         class TestSecond:
    
  410.             @method_decorator(decorators)
    
  411.             def method(self):
    
  412.                 return "hello world"
    
  413. 
    
  414.         self.assertEqual(TestFirst().method(), "hello world?!")
    
  415.         self.assertEqual(TestSecond().method(), "hello world?!")
    
  416. 
    
  417.     def test_invalid_non_callable_attribute_decoration(self):
    
  418.         """
    
  419.         @method_decorator on a non-callable attribute raises an error.
    
  420.         """
    
  421.         msg = (
    
  422.             "Cannot decorate 'prop' as it isn't a callable attribute of "
    
  423.             "<class 'Test'> (1)"
    
  424.         )
    
  425.         with self.assertRaisesMessage(TypeError, msg):
    
  426. 
    
  427.             @method_decorator(lambda: None, name="prop")
    
  428.             class Test:
    
  429.                 prop = 1
    
  430. 
    
  431.                 @classmethod
    
  432.                 def __module__(cls):
    
  433.                     return "tests"
    
  434. 
    
  435.     def test_invalid_method_name_to_decorate(self):
    
  436.         """
    
  437.         @method_decorator on a nonexistent method raises an error.
    
  438.         """
    
  439.         msg = (
    
  440.             "The keyword argument `name` must be the name of a method of the "
    
  441.             "decorated class: <class 'Test'>. Got 'nonexistent_method' instead"
    
  442.         )
    
  443.         with self.assertRaisesMessage(ValueError, msg):
    
  444. 
    
  445.             @method_decorator(lambda: None, name="nonexistent_method")
    
  446.             class Test:
    
  447.                 @classmethod
    
  448.                 def __module__(cls):
    
  449.                     return "tests"
    
  450. 
    
  451.     def test_wrapper_assignments(self):
    
  452.         """@method_decorator preserves wrapper assignments."""
    
  453.         func_name = None
    
  454.         func_module = None
    
  455. 
    
  456.         def decorator(func):
    
  457.             @wraps(func)
    
  458.             def inner(*args, **kwargs):
    
  459.                 nonlocal func_name, func_module
    
  460.                 func_name = getattr(func, "__name__", None)
    
  461.                 func_module = getattr(func, "__module__", None)
    
  462.                 return func(*args, **kwargs)
    
  463. 
    
  464.             return inner
    
  465. 
    
  466.         class Test:
    
  467.             @method_decorator(decorator)
    
  468.             def method(self):
    
  469.                 return "tests"
    
  470. 
    
  471.         Test().method()
    
  472.         self.assertEqual(func_name, "method")
    
  473.         self.assertIsNotNone(func_module)
    
  474. 
    
  475. 
    
  476. class XFrameOptionsDecoratorsTests(TestCase):
    
  477.     """
    
  478.     Tests for the X-Frame-Options decorators.
    
  479.     """
    
  480. 
    
  481.     def test_deny_decorator(self):
    
  482.         """
    
  483.         Ensures @xframe_options_deny properly sets the X-Frame-Options header.
    
  484.         """
    
  485. 
    
  486.         @xframe_options_deny
    
  487.         def a_view(request):
    
  488.             return HttpResponse()
    
  489. 
    
  490.         r = a_view(HttpRequest())
    
  491.         self.assertEqual(r.headers["X-Frame-Options"], "DENY")
    
  492. 
    
  493.     def test_sameorigin_decorator(self):
    
  494.         """
    
  495.         Ensures @xframe_options_sameorigin properly sets the X-Frame-Options
    
  496.         header.
    
  497.         """
    
  498. 
    
  499.         @xframe_options_sameorigin
    
  500.         def a_view(request):
    
  501.             return HttpResponse()
    
  502. 
    
  503.         r = a_view(HttpRequest())
    
  504.         self.assertEqual(r.headers["X-Frame-Options"], "SAMEORIGIN")
    
  505. 
    
  506.     def test_exempt_decorator(self):
    
  507.         """
    
  508.         Ensures @xframe_options_exempt properly instructs the
    
  509.         XFrameOptionsMiddleware to NOT set the header.
    
  510.         """
    
  511. 
    
  512.         @xframe_options_exempt
    
  513.         def a_view(request):
    
  514.             return HttpResponse()
    
  515. 
    
  516.         req = HttpRequest()
    
  517.         resp = a_view(req)
    
  518.         self.assertIsNone(resp.get("X-Frame-Options", None))
    
  519.         self.assertTrue(resp.xframe_options_exempt)
    
  520. 
    
  521.         # Since the real purpose of the exempt decorator is to suppress
    
  522.         # the middleware's functionality, let's make sure it actually works...
    
  523.         r = XFrameOptionsMiddleware(a_view)(req)
    
  524.         self.assertIsNone(r.get("X-Frame-Options", None))
    
  525. 
    
  526. 
    
  527. class HttpRequestProxy:
    
  528.     def __init__(self, request):
    
  529.         self._request = request
    
  530. 
    
  531.     def __getattr__(self, attr):
    
  532.         """Proxy to the underlying HttpRequest object."""
    
  533.         return getattr(self._request, attr)
    
  534. 
    
  535. 
    
  536. class NeverCacheDecoratorTest(SimpleTestCase):
    
  537.     @mock.patch("time.time")
    
  538.     def test_never_cache_decorator_headers(self, mocked_time):
    
  539.         @never_cache
    
  540.         def a_view(request):
    
  541.             return HttpResponse()
    
  542. 
    
  543.         mocked_time.return_value = 1167616461.0
    
  544.         response = a_view(HttpRequest())
    
  545.         self.assertEqual(
    
  546.             response.headers["Expires"],
    
  547.             "Mon, 01 Jan 2007 01:54:21 GMT",
    
  548.         )
    
  549.         self.assertEqual(
    
  550.             response.headers["Cache-Control"],
    
  551.             "max-age=0, no-cache, no-store, must-revalidate, private",
    
  552.         )
    
  553. 
    
  554.     def test_never_cache_decorator_expires_not_overridden(self):
    
  555.         @never_cache
    
  556.         def a_view(request):
    
  557.             return HttpResponse(headers={"Expires": "tomorrow"})
    
  558. 
    
  559.         response = a_view(HttpRequest())
    
  560.         self.assertEqual(response.headers["Expires"], "tomorrow")
    
  561. 
    
  562.     def test_never_cache_decorator_http_request(self):
    
  563.         class MyClass:
    
  564.             @never_cache
    
  565.             def a_view(self, request):
    
  566.                 return HttpResponse()
    
  567. 
    
  568.         request = HttpRequest()
    
  569.         msg = (
    
  570.             "never_cache didn't receive an HttpRequest. If you are decorating "
    
  571.             "a classmethod, be sure to use @method_decorator."
    
  572.         )
    
  573.         with self.assertRaisesMessage(TypeError, msg):
    
  574.             MyClass().a_view(request)
    
  575.         with self.assertRaisesMessage(TypeError, msg):
    
  576.             MyClass().a_view(HttpRequestProxy(request))
    
  577. 
    
  578.     def test_never_cache_decorator_http_request_proxy(self):
    
  579.         class MyClass:
    
  580.             @method_decorator(never_cache)
    
  581.             def a_view(self, request):
    
  582.                 return HttpResponse()
    
  583. 
    
  584.         request = HttpRequest()
    
  585.         response = MyClass().a_view(HttpRequestProxy(request))
    
  586.         self.assertIn("Cache-Control", response.headers)
    
  587.         self.assertIn("Expires", response.headers)
    
  588. 
    
  589. 
    
  590. class CacheControlDecoratorTest(SimpleTestCase):
    
  591.     def test_cache_control_decorator_http_request(self):
    
  592.         class MyClass:
    
  593.             @cache_control(a="b")
    
  594.             def a_view(self, request):
    
  595.                 return HttpResponse()
    
  596. 
    
  597.         msg = (
    
  598.             "cache_control didn't receive an HttpRequest. If you are "
    
  599.             "decorating a classmethod, be sure to use @method_decorator."
    
  600.         )
    
  601.         request = HttpRequest()
    
  602.         with self.assertRaisesMessage(TypeError, msg):
    
  603.             MyClass().a_view(request)
    
  604.         with self.assertRaisesMessage(TypeError, msg):
    
  605.             MyClass().a_view(HttpRequestProxy(request))
    
  606. 
    
  607.     def test_cache_control_decorator_http_request_proxy(self):
    
  608.         class MyClass:
    
  609.             @method_decorator(cache_control(a="b"))
    
  610.             def a_view(self, request):
    
  611.                 return HttpResponse()
    
  612. 
    
  613.         request = HttpRequest()
    
  614.         response = MyClass().a_view(HttpRequestProxy(request))
    
  615.         self.assertEqual(response.headers["Cache-Control"], "a=b")