Code

Ticket #2548: session_middleware.18.diff

File session_middleware.18.diff, 12.1 KB (added by SmileyChris, 6 years ago)

Updated to patch cleanly against [6980]

Line 
1Index: django/contrib/sessions/middleware.py
2===================================================================
3--- django/contrib/sessions/middleware.py       (revision 6980)
4+++ django/contrib/sessions/middleware.py       (working copy)
5@@ -26,12 +26,12 @@
6             if accessed:
7                 patch_vary_headers(response, ('Cookie',))
8             if modified or settings.SESSION_SAVE_EVERY_REQUEST:
9-                if settings.SESSION_EXPIRE_AT_BROWSER_CLOSE:
10+                if request.session.get_expire_at_browser_close():
11                     max_age = None
12                     expires = None
13                 else:
14-                    max_age = settings.SESSION_COOKIE_AGE
15-                    expires_time = time.time() + settings.SESSION_COOKIE_AGE
16+                    max_age = request.session.get_max_age()
17+                    expires_time = time.time() + max_age
18                     expires = cookie_date(expires_time)
19                 # Save the seesion data and refresh the client cookie.
20                 request.session.save()
21Index: django/contrib/sessions/tests.py
22===================================================================
23--- django/contrib/sessions/tests.py    (revision 6980)
24+++ django/contrib/sessions/tests.py    (working copy)
25@@ -88,6 +88,100 @@
26 
27 >>> s.pop('some key', 'does not exist')
28 'does not exist'
29+
30+#########################
31+# Custom session expiry #
32+#########################
33+
34+>>> from django.conf import settings
35+>>> from datetime import datetime, timedelta
36+
37+>>> td10 = timedelta(seconds=10)
38+
39+# A normal session has a max age equal to settings
40+>>> s.get_max_age() == settings.SESSION_COOKIE_AGE
41+True
42+
43+# So does a custom session with an idle expiration time of 0 (but it'll expire
44+# at browser close)
45+>>> s.set_expiry(0)
46+>>> s.get_max_age() == settings.SESSION_COOKIE_AGE
47+True
48+
49+# Custom session idle expiration time
50+>>> s.set_expiry(10)
51+>>> delta = s.get_expiry_date() - datetime.utcnow()
52+>>> delta.seconds in (9, 10)
53+True
54+>>> age = s.get_max_age()
55+>>> age in (9, 10)
56+True
57+
58+# Custom session fixed expiry date (timedelta)
59+>>> s.set_expiry(td10)
60+>>> delta = s.get_expiry_date() - datetime.utcnow()
61+>>> delta.seconds in (9, 10)
62+True
63+>>> age = s.get_max_age()
64+>>> age in (9, 10)
65+True
66+
67+# Custom session fixed expiry date (fixed datetime)
68+>>> s.set_expiry(datetime.utcnow() + td10)
69+>>> delta = s.get_expiry_date() - datetime.utcnow()
70+>>> delta.seconds in (9, 10)
71+True
72+>>> age = s.get_max_age()
73+>>> age in (9, 10)
74+True
75+
76+# Set back to default session age
77+>>> s.set_expiry(None)
78+>>> s.get_max_age() == settings.SESSION_COOKIE_AGE
79+True
80+
81+# Allow to set back to default session age even if no alternate has been set
82+>>> s.set_expiry(None)
83+
84+
85+# We're changing the setting then reverting back to the original setting at the
86+# end of these tests.
87+>>> original_expire_at_browser_close = settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
88+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = False
89+
90+# Custom session age
91+>>> s.set_expiry(10)
92+>>> s.get_expire_at_browser_close()
93+False
94+
95+# Custom expire-at-browser-close
96+>>> s.set_expiry(0)
97+>>> s.get_expire_at_browser_close()
98+True
99+
100+# Default session age
101+>>> s.set_expiry(None)
102+>>> s.get_expire_at_browser_close()
103+False
104+
105+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = True
106+
107+# Custom session age
108+>>> s.set_expiry(10)
109+>>> s.get_expire_at_browser_close()
110+False
111+
112+# Custom expire-at-browser-close
113+>>> s.set_expiry(0)
114+>>> s.get_expire_at_browser_close()
115+True
116+
117+# Default session age
118+>>> s.set_expiry(None)
119+>>> s.get_expire_at_browser_close()
120+True
121+
122+>>> settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = original_expire_at_browser_close
123 """
124 
125 if __name__ == '__main__':
126Index: django/contrib/sessions/backends/base.py
127===================================================================
128--- django/contrib/sessions/backends/base.py    (revision 6980)
129+++ django/contrib/sessions/backends/base.py    (working copy)
130@@ -4,6 +4,7 @@
131 import random
132 import sys
133 import time
134+from datetime import datetime, timedelta
135 from django.conf import settings
136 from django.core.exceptions import SuspiciousOperation
137 
138@@ -128,6 +129,54 @@
139 
140     _session = property(_get_session)
141 
142+    def get_max_age(self):
143+        expiry = self.get('_session_expiry')
144+        if not expiry:   # Checks both None and 0 cases
145+            return settings.SESSION_COOKIE_AGE
146+        if not isinstance(expiry, datetime):
147+            return expiry
148+        delta = expiry - datetime.utcnow()
149+        return delta.days * 86400 + delta.seconds
150+
151+    def get_expiry_date(self):
152+        "Returns the expiry date (in UTC)"
153+        expiry = self.get('_session_expiry', settings.SESSION_COOKIE_AGE)
154+        if isinstance(expiry, datetime):
155+            return expiry
156+        return datetime.utcnow() + timedelta(seconds=expiry)
157+
158+    def set_expiry(self, value):
159+        """
160+        Sets a custom expiration for the session. ``value`` can be an integer, a
161+        Python ``datetime`` or ``timedelta`` object or ``None``.
162+
163+        If ``value`` is an integer, the session will expire after that many
164+        seconds of inactivity. If set to ``0`` then the session will expire on
165+        browser close.
166+
167+        If ``value`` is a ``datetime`` or ``timedelta`` object, the session
168+        will expire at that specific future time (``datetime`` objects should be
169+        UTC).
170+
171+        If ``value`` is ``None``, the session uses the global session expiry
172+        policy.
173+        """
174+        if value is None:
175+            # Remove any custom expiration for this session.
176+            try:
177+                del self['_session_expiry']
178+            except KeyError:
179+                pass
180+            return
181+        if isinstance(value, timedelta):
182+            value = datetime.utcnow() + value
183+        self['_session_expiry'] = value
184+
185+    def get_expire_at_browser_close(self):
186+        if self.get('_session_expiry') is None:
187+            return settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
188+        return self.get('_session_expiry') == 0
189+
190     # Methods that child classes must implement.
191 
192     def exists(self, session_key):
193Index: django/contrib/sessions/backends/cache.py
194===================================================================
195--- django/contrib/sessions/backends/cache.py   (revision 6980)
196+++ django/contrib/sessions/backends/cache.py   (working copy)
197@@ -4,23 +4,23 @@
198 
199 class SessionStore(SessionBase):
200     """
201-    A cache-based session store.
202+    A cache-based session store.
203     """
204     def __init__(self, session_key=None):
205         self._cache = cache
206         super(SessionStore, self).__init__(session_key)
207-       
208+
209     def load(self):
210         session_data = self._cache.get(self.session_key)
211         return session_data or {}
212 
213     def save(self):
214-        self._cache.set(self.session_key, self._session, settings.SESSION_COOKIE_AGE)
215+        self._cache.set(self.session_key, self._session, self.get_max_age())
216 
217     def exists(self, session_key):
218         if self._cache.get(session_key):
219             return True
220         return False
221-       
222+
223     def delete(self, session_key):
224         self._cache.delete(session_key)
225\ No newline at end of file
226Index: django/contrib/sessions/backends/db.py
227===================================================================
228--- django/contrib/sessions/backends/db.py      (revision 6980)
229+++ django/contrib/sessions/backends/db.py      (working copy)
230@@ -10,16 +10,16 @@
231     """
232     def __init__(self, session_key=None):
233         super(SessionStore, self).__init__(session_key)
234-   
235+
236     def load(self):
237         try:
238             s = Session.objects.get(
239-                session_key = self.session_key,
240+                session_key = self.session_key,
241                 expire_date__gt=datetime.datetime.now()
242             )
243             return self.decode(s.session_data)
244         except (Session.DoesNotExist, SuspiciousOperation):
245-           
246+
247             # Create a new session_key for extra security.
248             self.session_key = self._get_new_session_key()
249             self._session_cache = {}
250@@ -27,21 +27,21 @@
251             # Save immediately to minimize collision
252             self.save()
253             return {}
254-           
255+
256     def exists(self, session_key):
257         try:
258             Session.objects.get(session_key=session_key)
259         except Session.DoesNotExist:
260             return False
261         return True
262-           
263+
264     def save(self):
265         Session.objects.create(
266             session_key = self.session_key,
267             session_data = self.encode(self._session),
268-            expire_date = datetime.datetime.now() + datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE)
269+            expire_date = self.get_expiry_date()
270         )
271-   
272+
273     def delete(self, session_key):
274         try:
275             Session.objects.get(session_key=session_key).delete()
276Index: AUTHORS
277===================================================================
278--- AUTHORS     (revision 6980)
279+++ AUTHORS     (working copy)
280@@ -327,7 +327,7 @@
281     tstromberg@google.com
282     Makoto Tsuyuki <mtsuyuki@gmail.com>
283     tt@gurgle.no
284-    Amit Upadhyay
285+    Amit Upadhyay <http://www.amitu.com/blog/>
286     Geert Vanderkelen
287     I.S. van Oostveen <v.oostveen@idca.nl>
288     viestards.lists@gmail.com
289Index: docs/sessions.txt
290===================================================================
291--- docs/sessions.txt   (revision 6980)
292+++ docs/sessions.txt   (working copy)
293@@ -80,19 +80,24 @@
294 It implements the following standard dictionary methods:
295 
296     * ``__getitem__(key)``
297+
298       Example: ``fav_color = request.session['fav_color']``
299 
300     * ``__setitem__(key, value)``
301+
302       Example: ``request.session['fav_color'] = 'blue'``
303 
304     * ``__delitem__(key)``
305+
306       Example: ``del request.session['fav_color']``. This raises ``KeyError``
307       if the given ``key`` isn't already in the session.
308 
309     * ``__contains__(key)``
310+
311       Example: ``'fav_color' in request.session``
312 
313     * ``get(key, default=None)``
314+
315       Example: ``fav_color = request.session.get('fav_color', 'red')``
316 
317     * ``keys()``
318@@ -101,7 +106,7 @@
319 
320     * ``setdefault()`` (**New in Django development version**)
321 
322-It also has these three methods:
323+It also has these methods:
324 
325     * ``set_test_cookie()``
326       Sets a test cookie to determine whether the user's browser supports
327@@ -118,6 +123,45 @@
328     * ``delete_test_cookie()``
329       Deletes the test cookie. Use this to clean up after yourself.
330 
331+    * ``set_expiry(value)``
332+
333+      **New in Django development version**
334+
335+      Sets a custom expiration for the session.
336+
337+      If ``value`` is an integer, the session will expire after that many
338+      seconds of inactivity. If set to ``0`` then the session will expire when
339+      the user's browser is closed.
340+
341+      If ``value`` is a ``datetime`` or ``timedelta`` object, the session will
342+      expire at that specific time (``datetime`` objects must be in UTC).
343+
344+      If ``value`` is ``None``, the session reverts to using the global session
345+      expiry policy.
346+
347+    * ``get_max_age()``
348+
349+      **New in Django development version**
350+
351+      Returns the number of seconds until this session expires. For sessions
352+      with no custom expiration (or those set to expire at browser close), this
353+      will equal ``settings.SESSION_COOKIE_AGE``.
354+
355+    * ``get_expiry_date()``
356+
357+      **New in Django development version**
358+
359+      Returns the date this session will expire. For sessions with no custom
360+      expiration (or those set to expire at browser close), this will equal the
361+      date ``settings.SESSION_COOKIE_AGE`` seconds from now.
362+
363+    * ``get_expire_at_browser_close()``
364+
365+      **New in Django development version**
366+
367+      Returns either ``True`` or ``False``, depending on whether this session
368+      will expire when the user's browser is closed.
369+
370 You can edit ``request.session`` at any point in your view. You can edit it
371 multiple times.
372 
373@@ -278,6 +322,12 @@
374 her browser. Use this if you want people to have to log in every time they open
375 a browser.
376 
377+**New in Django development version**
378+
379+This setting is a global default and can be overwritten by explicitly calling
380+``request.session.set_expiry()`` as described above in
381+`using sessions in views`_.
382+
383 Clearing the session table
384 ==========================
385