Skip to content

Commit 9b549e9

Browse files
committed
add IDLE_TIME check
1 parent da68e02 commit 9b549e9

File tree

5 files changed

+133
-12
lines changed

5 files changed

+133
-12
lines changed

README.rst

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ Append to `settings` middlewares:
2525
'django_auto_logout.middleware.auto_logout',
2626
)
2727
28+
Logout in case of idle
29+
----------------------
30+
31+
Logout a user if there are no requests for a long time.
32+
33+
Add to `settings`:
34+
35+
.. code:: python
36+
37+
AUTO_LOGOUT = {'IDLE_TIME': 600} # logout after 10 minutes of downtime
38+
39+
2840
Limit session time
2941
------------------
3042

@@ -35,3 +47,18 @@ Add to `settings`:
3547
.. code:: python
3648
3749
AUTO_LOGOUT = {'SESSION_TIME': 3600}
50+
51+
Combine configurations
52+
----------------------
53+
54+
You can combine previous configurations. For example, you may want to logout a user
55+
in case of downtime (5 minutes or more) and not allow working within one session
56+
for more than half an hour:
57+
58+
59+
.. code:: python
60+
61+
AUTO_LOGOUT = {
62+
'IDLE_TIME': 300, # 5 minutes
63+
'SESSION_TIME': 1800, # 30 minutes
64+
}

django_auto_logout/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.1.1'
1+
__version__ = '0.2.0'

django_auto_logout/middleware.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,29 @@ def _auto_logout(request: HttpRequest, options):
1919
else:
2020
now = datetime.now()
2121

22-
if 'SESSION_TIME' in options:
23-
ttl = options['SESSION_TIME']
24-
should_logout = user.last_login < now - timedelta(seconds=ttl)
25-
logger.debug('Check SESSION_TIME: %s < %s (%s)', user.last_login, now, should_logout)
22+
if options.get('SESSION_TIME') is not None:
23+
ttl = timedelta(seconds=options['SESSION_TIME'])
24+
time_expired = user.last_login < now - ttl
25+
should_logout |= time_expired
26+
logger.debug('Check SESSION_TIME: %s < %s (%s)', user.last_login, now, time_expired)
27+
28+
if options.get('IDLE_TIME') is not None:
29+
ttl = timedelta(seconds=options['IDLE_TIME'])
30+
31+
if 'django_auto_logout_last_touch' in request.session:
32+
last_touch = datetime.fromisoformat(request.session['django_auto_logout_last_touch'])
33+
else:
34+
last_touch = now
35+
request.session['django_auto_logout_last_touch'] = last_touch.isoformat()
36+
37+
time_expired = last_touch < now - ttl
38+
should_logout |= time_expired
39+
logger.debug('Check IDLE_TIME: %s < %s (%s)', last_touch, now, time_expired)
40+
41+
if should_logout:
42+
del request.session['django_auto_logout_last_touch']
43+
else:
44+
request.session['django_auto_logout_last_touch'] = now.isoformat()
2645

2746
if should_logout:
2847
logger.debug('Logout user %s', user)

example/example/settings.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
'django.contrib.auth.middleware.AuthenticationMiddleware',
5050
'django.contrib.messages.middleware.MessageMiddleware',
5151
'django.middleware.clickjacking.XFrameOptionsMiddleware',
52+
5253
'django_auto_logout.middleware.auto_logout',
5354
]
5455

@@ -161,3 +162,7 @@
161162

162163

163164
# DJANGO AUTO LOGIN
165+
AUTO_LOGOUT = {
166+
'IDLE_TIME': 300, # 5 minutes
167+
'SESSION_TIME': 1800, # 30 minutes
168+
}

example/some_app_login_required/tests.py

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,93 @@ def setUp(self):
1212
self.superuser = UserModel.objects.create_superuser('superuser', 'superuser@localhost', 'pass')
1313
self.url = '/login-required/'
1414

15-
def _logout_session_time(self):
16-
settings.AUTO_LOGOUT = {'SESSION_TIME': 1}
15+
def assertLoginRequiredIsOk(self):
16+
resp = self.client.get(self.url)
17+
self.assertContains(resp, 'login required view', msg_prefix='Fine with authorized')
18+
19+
def assertLoginRequiredRedirect(self):
1720
resp = self.client.get(self.url)
1821
self.assertRedirects(resp, f'{settings.LOGIN_URL}?next={self.url}', msg_prefix='Redirect for anonymous')
1922

23+
def _logout_session_time(self):
24+
settings.AUTO_LOGOUT = {'SESSION_TIME': 1}
25+
self.assertLoginRequiredRedirect()
26+
2027
self.client.force_login(self.user)
21-
resp = self.client.get(self.url)
22-
self.assertContains(resp, 'login required view', msg_prefix='Fine with authorized')
28+
self.assertLoginRequiredIsOk()
2329

2430
sleep(1)
25-
resp = self.client.get(self.url)
26-
self.assertRedirects(resp, f'{settings.LOGIN_URL}?next={self.url}', msg_prefix='Logout on session time expired')
31+
self.assertLoginRequiredRedirect()
2732

2833
def test_logout_session_time(self):
2934
settings.USE_TZ = False
3035
self._logout_session_time()
3136

32-
def test_logout_session_time_using_tz(self):
37+
def test_logout_session_time_using_tz_utc(self):
38+
settings.USE_TZ = True
39+
self._logout_session_time()
40+
41+
def test_logout_session_time_using_tz_non_utc(self):
3342
settings.USE_TZ = True
43+
settings.TIME_ZONE = 'Asia/Yekaterinburg'
3444
self._logout_session_time()
45+
46+
def test_logout_idle_time_no_idle(self):
47+
settings.AUTO_LOGOUT = {'IDLE_TIME': 1}
48+
self.client.force_login(self.user)
49+
self.assertLoginRequiredIsOk()
50+
51+
for _ in range(10):
52+
sleep(0.5)
53+
self.assertLoginRequiredIsOk()
54+
55+
def test_logout_idle_time(self):
56+
settings.AUTO_LOGOUT = {'IDLE_TIME': 1}
57+
self.client.force_login(self.user)
58+
self.assertLoginRequiredIsOk()
59+
60+
sleep(1.5)
61+
self.assertLoginRequiredRedirect()
62+
63+
def test_combine_idle_and_session_time(self):
64+
settings.AUTO_LOGOUT = {
65+
'IDLE_TIME': 1,
66+
'SESSION_TIME': 2,
67+
}
68+
69+
self.client.force_login(self.user)
70+
self.assertLoginRequiredIsOk()
71+
72+
sleep(0.5)
73+
self.assertLoginRequiredIsOk()
74+
sleep(0.5)
75+
self.assertLoginRequiredIsOk()
76+
sleep(0.5)
77+
self.assertLoginRequiredIsOk()
78+
sleep(0.5)
79+
self.assertLoginRequiredRedirect()
80+
81+
def test_combine_idle_and_session_time_but_session_less_than_idle(self):
82+
settings.AUTO_LOGOUT = {
83+
'IDLE_TIME': 2,
84+
'SESSION_TIME': 1,
85+
}
86+
87+
self.client.force_login(self.user)
88+
self.assertLoginRequiredIsOk()
89+
sleep(0.5)
90+
self.assertLoginRequiredIsOk()
91+
sleep(0.5)
92+
self.assertLoginRequiredRedirect()
93+
94+
self.client.force_login(self.user)
95+
self.assertLoginRequiredIsOk()
96+
sleep(0.5)
97+
self.assertLoginRequiredIsOk()
98+
sleep(0.5)
99+
self.assertLoginRequiredRedirect()
100+
101+
self.client.force_login(self.user)
102+
self.assertLoginRequiredIsOk()
103+
sleep(1)
104+
self.assertLoginRequiredRedirect()

0 commit comments

Comments
 (0)