forked from dj-stripe/dj-stripe
-
Notifications
You must be signed in to change notification settings - Fork 0
/
middleware.py
97 lines (73 loc) · 3.21 KB
/
middleware.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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
"""
dj-stripe middleware
Refer to SubscriptionPaymentMiddleware docstring for more info.
"""
import fnmatch
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.shortcuts import redirect
from django.urls import resolve
from django.utils.deprecation import MiddlewareMixin
from .utils import subscriber_has_active_subscription
class SubscriptionPaymentMiddleware(MiddlewareMixin):
"""
Used to redirect users from subcription-locked request destinations.
Rules:
- "(app_name)" means everything from this app is exempt
- "[namespace]" means everything with this name is exempt
- "namespace:name" means this namespaced URL is exempt
- "name" means this URL is exempt
- The entire djstripe namespace is exempt
- If settings.DEBUG is True, then django-debug-toolbar is exempt
- A 'fn:' prefix means the rest of the URL is fnmatch'd.
Example:
```py
DJSTRIPE_SUBSCRIPTION_REQUIRED_EXCEPTION_URLS = (
"[blogs]", # Anything in the blogs namespace
"products:detail", # A ProductDetail view you want shown to non-payers
"home", # Site homepage
"fn:/accounts*", # anything in the accounts/ URL path
)
```
"""
def process_request(self, request):
"""Check the subscriber's subscription status.
Returns early if request does not outlined in this middleware's docstring.
"""
if self.is_matching_rule(request):
return
return self.check_subscription(request)
def is_matching_rule(self, request):
"""Check according to the rules defined in the class docstring."""
# First, if in DEBUG mode and with django-debug-toolbar, we skip
# this entire process.
from .settings import SUBSCRIPTION_REQUIRED_EXCEPTION_URLS
if settings.DEBUG and request.path.startswith("/__debug__"):
return True
exempt_urls = list(SUBSCRIPTION_REQUIRED_EXCEPTION_URLS) + ["[djstripe]"]
# Second we check against matches
match = resolve(
request.path, getattr(request, "urlconf", settings.ROOT_URLCONF)
)
if "({0})".format(match.app_name) in exempt_urls:
return True
if "[{0}]".format(match.namespace) in exempt_urls:
return True
if "{0}:{1}".format(match.namespace, match.url_name) in exempt_urls:
return True
if match.url_name in exempt_urls:
return True
# Third, we check wildcards:
for exempt in [x for x in exempt_urls if x.startswith("fn:")]:
exempt = exempt.replace("fn:", "")
if fnmatch.fnmatch(request.path, exempt):
return True
return False
def check_subscription(self, request):
"""Redirect to the subscribe page if the user lacks an active subscription."""
from .settings import SUBSCRIPTION_REDIRECT, subscriber_request_callback
subscriber = subscriber_request_callback(request)
if not subscriber_has_active_subscription(subscriber):
if not SUBSCRIPTION_REDIRECT:
raise ImproperlyConfigured("DJSTRIPE_SUBSCRIPTION_REDIRECT is not set.")
return redirect(SUBSCRIPTION_REDIRECT)