forked from jazzband/django-downloadview
-
Notifications
You must be signed in to change notification settings - Fork 0
/
decorators.py
76 lines (54 loc) · 2.45 KB
/
decorators.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
"""View decorators.
See also decorators provided by server-specific modules, such as
:func:`django_downloadview.nginx.x_accel_redirect`.
"""
from functools import wraps
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.core.signing import BadSignature, SignatureExpired, TimestampSigner
class DownloadDecorator(object):
"""View decorator factory to apply middleware to ``view_func``'s response.
Middleware instance is built from ``middleware_factory`` with ``*args`` and
``**kwargs``. Middleware factory is typically a class, such as some
:py:class:`django_downloadview.BaseDownloadMiddleware` subclass.
Response is built from view, then the middleware's ``process_response``
method is applied on response.
"""
def __init__(self, middleware_factory):
"""Create a download view decorator."""
self.middleware_factory = middleware_factory
def __call__(self, view_func, *middleware_args, **middleware_kwargs):
"""Return ``view_func`` decorated with response middleware."""
def decorated(request, *view_args, **view_kwargs):
"""Return view's response modified by middleware."""
response = view_func(request, *view_args, **view_kwargs)
middleware = self.middleware_factory(*middleware_args, **middleware_kwargs)
return middleware.process_response(request, response)
return decorated
def _signature_is_valid(request):
"""
Validator that raises a PermissionDenied error on invalid and
mismatching signatures.
"""
signer = TimestampSigner()
signature = request.GET.get("X-Signature")
expiration = getattr(settings, "DOWNLOADVIEW_URL_EXPIRATION", None)
try:
signature_path = signer.unsign(signature, max_age=expiration)
except SignatureExpired as e:
raise PermissionDenied("Signature expired") from e
except BadSignature as e:
raise PermissionDenied("Signature invalid") from e
except Exception as e:
raise PermissionDenied("Signature error") from e
if request.path != signature_path:
raise PermissionDenied("Signature mismatch")
def signature_required(function):
"""
Decorator that checks for X-Signature query parameter to authorize access to views.
"""
@wraps(function)
def decorator(request, *args, **kwargs):
_signature_is_valid(request)
return function(request, *args, **kwargs)
return decorator