-
Notifications
You must be signed in to change notification settings - Fork 141
/
Copy pathmiddleware.py
109 lines (86 loc) · 3.61 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
"""
SessionSecurityMiddleware is the heart of the security that this application
attemps to provide.
To install this middleware, add to your ``settings.MIDDLEWARE_CLASSES``::
'session_security.middleware.SessionSecurityMiddleware'
Make sure that it is placed **after** authentication middlewares.
"""
from datetime import datetime, timedelta
import django
from django.contrib.auth import logout
from django.contrib import messages
try: # Django 2.0
from django.urls import reverse, resolve, Resolver404
except: # Django < 2.0
from django.core.urlresolvers import reverse, resolve, Resolver404
try:
from django.utils.deprecation import MiddlewareMixin
except ImportError: # Django < 1.10
# Works perfectly for everyone using MIDDLEWARE_CLASSES
MiddlewareMixin = object
from .utils import get_last_activity, set_last_activity
from .settings import EXPIRE_AFTER, PASSIVE_URLS, PASSIVE_URL_NAMES, LOGOUT_MESSAGE
class SessionSecurityMiddleware(MiddlewareMixin):
"""
In charge of maintaining the real 'last activity' time, and log out the
user if appropriate.
"""
def is_passive_request(self, request):
""" Should we skip activity update on this URL/View. """
if request.path in PASSIVE_URLS:
return True
try:
match = resolve(request.path)
# TODO: check namespaces too
if match.url_name in PASSIVE_URL_NAMES:
return True
except Resolver404:
pass
return False
def get_expire_seconds(self, request):
"""Return time (in seconds) before the user should be logged out."""
return EXPIRE_AFTER
def process_request(self, request):
""" Update last activity time or logout. """
if django.VERSION < (1, 10):
is_authenticated = request.user.is_authenticated()
else:
is_authenticated = request.user.is_authenticated
if not is_authenticated:
return
now = datetime.now()
if '_session_security' not in request.session:
set_last_activity(request.session, now)
return
delta = now - get_last_activity(request.session)
expire_seconds = self.get_expire_seconds(request)
if delta >= timedelta(seconds=expire_seconds):
if LOGOUT_MESSAGE:
messages.add_message(request, messages.WARNING, LOGOUT_MESSAGE)
logout(request)
elif (request.path == reverse('session_security_ping') and
'idleFor' in request.GET):
self.update_last_activity(request, now)
elif not self.is_passive_request(request):
set_last_activity(request.session, now)
def update_last_activity(self, request, now):
"""
If ``request.GET['idleFor']`` is set, check if it refers to a more
recent activity than ``request.session['_session_security']`` and
update it in this case.
"""
last_activity = get_last_activity(request.session)
server_idle_for = (now - last_activity).seconds
# Gracefully ignore non-integer values
try:
client_idle_for = int(request.GET['idleFor'])
except ValueError:
return
# Disallow negative values, causes problems with delta calculation
if client_idle_for < 0:
client_idle_for = 0
if client_idle_for < server_idle_for:
# Client has more recent activity than we have in the session
last_activity = now - timedelta(seconds=client_idle_for)
# Update the session
set_last_activity(request.session, last_activity)