-
Notifications
You must be signed in to change notification settings - Fork 29
/
middleware.py
181 lines (148 loc) · 6.71 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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
from __future__ import unicode_literals
from django.utils import six
from django.conf import settings
from django.core.urlresolvers import reverse, resolve
from django.shortcuts import redirect
from django.db.models import Q
from hunger.models import InvitationCode, Invitation
from hunger.utils import setting, now
class BetaMiddleware(object):
"""
Add this to your ``MIDDLEWARE_CLASSES`` make all views except for
those in the account application require that a user be logged in.
This can be a quick and easy way to restrict views on your site,
particularly if you remove the ability to create accounts.
**Settings:**
``HUNGER_ENABLE_BETA``
Whether or not the beta middleware should be used. If set to
`False` the BetaMiddleware middleware will be ignored and the
request will be returned. This is useful if you want to
disable privatebeta on a development machine. Default is
`True`.
``HUNGER_ALWAYS_ALLOW_VIEWS``
A list of full view names that should always pass through.
``HUNGER_ALWAYS_ALLOW_MODULES``
A list of modules that should always pass through. All
views in ``django.contrib.auth.views``, ``django.views.static``
and ``hunger.views`` will pass through.
``HUNGER_REDIRECT``
The redirect when not in beta.
"""
def __init__(self):
self.enable_beta = setting('HUNGER_ENABLE')
self.always_allow_views = setting('HUNGER_ALWAYS_ALLOW_VIEWS')
self.always_allow_modules = setting('HUNGER_ALWAYS_ALLOW_MODULES')
self.redirect = setting('HUNGER_REDIRECT')
self.allow_flatpages = setting('HUNGER_ALLOW_FLATPAGES')
def process_view(self, request, view_func, view_args, view_kwargs):
if not self.enable_beta:
return
if (request.path in self.allow_flatpages or
(getattr(settings, 'APPEND_SLASH', True) and
'%s/' % request.path in self.allow_flatpages)):
from django.contrib.flatpages.views import flatpage
return flatpage(request, request.path_info)
whitelisted_modules = ['django.contrib.auth.views',
'django.contrib.admin.sites',
'django.views.static',
'django.contrib.staticfiles.views']
# All hunger views, except NotBetaView, are off limits until in beta
whitelisted_views = ['hunger.views.NotBetaView',
'hunger.views.verify_invite',
'hunger.views.InvalidView']
short_name = view_func.__class__.__name__
if short_name == 'function':
short_name = view_func.__name__
view_name = self._get_view_name(request)
full_view_name = '%s.%s' % (view_func.__module__, short_name)
if self.always_allow_modules:
whitelisted_modules += self.always_allow_modules
if '%s' % view_func.__module__ in whitelisted_modules:
return
if self.always_allow_views:
whitelisted_views += self.always_allow_views
if (full_view_name in whitelisted_views or
view_name in whitelisted_views):
return
if not request.user.is_authenticated():
# Ask anonymous user to log in if trying to access in-beta view
return redirect(setting('LOGIN_URL'))
if request.user.is_staff:
return
# Prevent queries by caching in_beta status in session
if request.session.get('hunger_in_beta'):
return
cookie_code = request.COOKIES.get('hunger_code')
invitations = Invitation.objects.filter(
Q(user=request.user) |
Q(email=request.user.email)
).select_related('code')
# User already in the beta - cache in_beta in session
if any([i.used for i in invitations if i.invited]):
request.session['hunger_in_beta'] = True
return
# User has been invited - use the invitation and place in beta.
activates = [i for i in invitations if i.invited and not i.used]
# Check for matching cookie code if available.
if cookie_code:
for invitation in activates:
if invitation.code and invitation.code.code == cookie_code:
# Invitation may be attached to email
invitation.user = request.user
invitation.used = now()
invitation.save()
request.session['hunger_in_beta'] = True
request._hunger_delete_cookie = True
return
# No cookie - let's just choose the first invitation if it exists
if activates:
invitation = activates[0]
# Invitation may be attached to email
invitation.user = request.user
invitation.used = now()
invitation.save()
request.session['hunger_in_beta'] = True
return
if not cookie_code:
if not invitations:
invitation = Invitation(user=request.user)
invitation.save()
return redirect(self.redirect)
# No invitation, all we have is this cookie code
try:
code = InvitationCode.objects.get(code=cookie_code,
num_invites__gt=0)
except InvitationCode.DoesNotExist:
request._hunger_delete_cookie = True
return redirect(reverse('hunger-invalid', args=(cookie_code,)))
right_now = now()
if code.private:
# User is trying to use a valid private code, but has no
# authority to use it (neither via username nor email)
request._hunger_delete_cookie = True
return redirect(reverse('hunger-invalid', args=(cookie_code,)))
else:
invitation = Invitation(user=request.user,
code=code,
invited=right_now,
used=right_now)
code.num_invites -= 1
invitation.save()
code.save()
return
def process_response(self, request, response):
if getattr(request, '_hunger_delete_cookie', False):
if six.PY3:
code = 'hunger_code'
else:
code = u'hunger_code'.encode('utf-8')
response.delete_cookie(code)
return response
@staticmethod
def _get_view_name(request):
"""Return the urlpattern name."""
if hasattr(request, 'resolver_match'):
# Django >= 1.5
return request.resolver_match.view_name
match = resolve(request.path)
return match.url_name