Session incorrectly times out early if there is a long running request #1

Open
yscumc opened this Issue Mar 25, 2013 · 32 comments

Projects

None yet

2 participants

@yscumc
yscumc commented Mar 25, 2013

Using:

Firefox 19.0.2 with Firebug
Django (1.5)
django-session-security (2.0.3)

Problem:

If there's a long running request, the last activity stored in request.session['_session_security'] is immediately set during the beginning of the request, but it is not returned to the client browser until the request is completed 45 seconds later. Since the session values are stored as cookies on the client side, all the ping requests during this 45 seconds which updated the last activity will be overwritten.
When the long running request is finally completed, the last activity stored in request.session['_session_security'] will be set to 45 seconds ago. If the next request is to the ping view, then all will be fine as the ping view updates the last activity. However, the the request request is another long running request, or something else which does NOT have the idleFor parameter (which means anything other than the ping request), then delta.seconds would be larger than EXPIRE_AFTER and logout(request) would be called. Any subsequent ping requests would return "logout" as a response, although the page the user is viewing would not immediately be logged out.

To reproduce:

  1. Use these settings:

Settings for session_security

SESSION_SECURITY_WARN_AFTER = 10 # Default 540
SESSION_SECURITY_EXPIRE_AFTER = 30 # Default 600
SESSION_EXPIRE_AT_BROWSER_CLOSE = True

  1. Open Firebug and enable Net to see the AJAX connections
  2. Create a page which requests something via AJAX that takes about 45 seconds for the request to complete, which upon completion would create another identical request.
  3. Log in and move the mouse when the warning comes on, then wait until the next warning comes on and move the mouse again.

Here's an example of the requests to the ping view found from Firebug

First Request

GET /session_security/ping/?csrfmiddlewaretoken=ZJgthGOkE5JTR1sS5uxYsc5fn9wd2vgG&idleFor=0&_=1364238435092 HTTP/1.1
Host: localhost:8000
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Referer: http://localhost:8000/home/
Cookie: csrftoken=ZJgthGOkE5JTR1sS5uxYsc5fn9wd2vgG; sessionid=py3o4zs90uiojw3ylyb7hxo287n40om4
Connection: keep-alive

HTTP/1.0 200 OK
Date: Mon, 25 Mar 2013 19:07:15 GMT
Server: WSGIServer/0.1 Python/2.7.3
Vary: Cookie
Content-Type: text/html; charset=utf-8
Set-Cookie: sessionid=py3o4zs90uiojw3ylyb7hxo287n40om4; httponly; Path=/

0

Second Request, after long running request finished and started again

GET /session_security/ping/?csrfmiddlewaretoken=ZJgthGOkE5JTR1sS5uxYsc5fn9wd2vgG&idleFor=1&_=1364238445135 HTTP/1.1
Host: localhost:8000
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Referer: http://localhost:8000/home/
Cookie: csrftoken=ZJgthGOkE5JTR1sS5uxYsc5fn9wd2vgG; sessionid=py3o4zs90uiojw3ylyb7hxo287n40om4
Connection: keep-alive

HTTP/1.0 200 OK
Date: Mon, 25 Mar 2013 19:07:25 GMT
Server: WSGIServer/0.1 Python/2.7.3
Vary: Cookie
Content-Type: text/html; charset=utf-8
Set-Cookie: sessionid=ipub9bxy50x3kleq5w2ekzbrxz0gdfa1; httponly; Path=/

logout

Two things are wrong here:

  1. After receiving the logout response, the page is not immediately logged out. As long as I continue to move my mouse in the page when the warning comes up, I can stay on the page indefinitely, even though all the subsequent AJAX responses say logout. However, refreshing the page redirects me to the login screen, which means I was logged out.
  2. The logout response is incorrect since the "idleFor" is only 1 and it's been only 10 seconds since the last request. The logout really should not be triggered for another 29 seconds.
@jpic jpic added a commit that referenced this issue Mar 28, 2013
@jpic jpic Coded the use case described in #1 9e73568
@jpic
Member
jpic commented Mar 28, 2013

By default, django stores session data in the database. However, it probably only does so at the end of the request. To fix this, we could make the middleware force a database write i think with request.session.write() but I don't think we should do that by default.

Ideally, SESSION_SECURITY_EXPIRE_AFTER should be higher than the longest possible request on your website. Can't you set it that way ?

I could reproduce the problem on /sleep/ (new url in the test project). Did not test it with your pull request thought.

What do you suggest ?

Also, thanks a lot for your feedback !

@yscumc
yscumc commented Mar 28, 2013

Yes you are right, I assumed the session data was stored on the client side because of the behavior, but I just confirmed that the cookie only contains a session ID. I don't really have any suggestions and I was hoping you would lol. I have increased SESSION_SECURITY_EXPIRE_AFTER on my site and I was using the low values just for testing.

I think this can probably be documented as a trivial bug that won't need to be fixed as long as SESSION_SECURITY_EXPIRE_AFTER is longer than the timeout for a request.

The tests doesn't run on my machine because it's missing selenium and I haven't had a chance to figure out how to set that up yet:

running test
ImportError: No module named selenium.webdriver.common.keys
@jpic
Member
jpic commented Mar 28, 2013

On Thu, Mar 28, 2013 at 5:52 PM, yscumc notifications@github.com wrote:

I don't really have any suggestions and I was hoping you would lol. I have increased SESSION_SECURITY_EXPIRE_AFTER on my site and I was using the low values just for testing.

I think this can probably be documented as a trivial bug that won't need to be fixed as long as SESSION_SECURITY_EXPIRE_AFTER is longer than the timeout for a request.

I'm thinking that maybe we could have a setting that forces writing
the session last activity at the beginning of the request.

This should be documented indeed.

The tests doesn't run on my machine because it's missing selenium and I haven't had a chance to figure out how to set that up yet:

running test
ImportError: No module named selenium.webdriver.common.keys

Oops, sounds like you need to pip install selenium :)

@yscumc yscumc referenced this issue Mar 29, 2013
Merged

3 bug fixes #3

@yscumc
yscumc commented Mar 29, 2013

Wow pip makes working with python easier than pie =)

Turns out I also needed unittest_data_provider. Installed it and it ran with my FF 19.0.2.

Not sure if this was supposed to happen but four tests failed:

======================================================================
FAIL: test_double_hide_warning (session_security.tests.script.ScriptTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "E:\Projects\Third Party\django-session-security\session_security\tests\script.py", line 154, in test_double_hide
_warning
    self.assertWarningShows(9)
  File "E:\Projects\Third Party\django-session-security\session_security\tests\script.py", line 73, in assertWarningShow
s
    self.fail('Warning did not make it into DOM')
AssertionError: Warning did not make it into DOM

======================================================================
FAIL: test_double_window_inactivity (session_security.tests.script.ScriptTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "E:\Projects\Third Party\django-session-security\session_security\tests\script.py", line 139, in test_double_wind
ow_inactivity
    self.assertWarningShows(9)
  File "E:\Projects\Third Party\django-session-security\session_security\tests\script.py", line 73, in assertWarningShow
s
    self.fail('Warning did not make it into DOM')
AssertionError: Warning did not make it into DOM

======================================================================
FAIL: test_single_hide_warning (session_security.tests.script.ScriptTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "E:\Projects\Third Party\django-session-security\session_security\tests\script.py", line 131, in test_single_hide
_warning
    self.assertWarningShows(9)
  File "E:\Projects\Third Party\django-session-security\session_security\tests\script.py", line 73, in assertWarningShow
s
    self.fail('Warning did not make it into DOM')
AssertionError: Warning did not make it into DOM

======================================================================
FAIL: test_single_window_inactivity (session_security.tests.script.ScriptTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "E:\Projects\Third Party\django-session-security\session_security\tests\script.py", line 118, in test_single_wind
ow_inactivity
    self.assertWarningShows(9)
  File "E:\Projects\Third Party\django-session-security\session_security\tests\script.py", line 73, in assertWarningShow
s
    self.fail('Warning did not make it into DOM')
AssertionError: Warning did not make it into DOM

----------------------------------------------------------------------
Ran 11 tests in 149.273s

FAILED (failures=4)
Destroying test database for alias 'default'...

First time I used selenium... pretty cool stuff.

@jpic
Member
jpic commented Mar 30, 2013

Did you run it from your own project ?

It should be run from the test_project in this repo ...

... but yeah, it tries to be good, here's a nice link if you're thirsty of knowledge ;) http://www.tdd-django-tutorial.com/

@yscumc
yscumc commented Apr 1, 2013

Nope, I ran it within django-session-security as setup.py test. I did get a lot of errors like the following though:

Traceback (most recent call last):
  File "C:\Python27\lib\wsgiref\handlers.py", line 85, in run
    self.result = application(self.environ, self.start_response)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py", line 73, in __call__
    return super(StaticFilesHandler, self).__call__(environ, start_response)
  File "C:\Python27\lib\site-packages\django\core\handlers\wsgi.py", line 255, in __call__
    response = self.get_response(request)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py", line 63, in get_response
    return self.serve(request)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py", line 56, in serve
    return serve(request, self.file_path(request.path), insecure=True)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\views.py", line 38, in serve
    absolute_path = finders.find(normalized_path)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\finders.py", line 239, in find
    result = finder.find(path, all=all)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\finders.py", line 79, in find
    matched_path = self.find_location(root, path, prefix)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\finders.py", line 96, in find_location
    path = safe_join(root, path)
  File "C:\Python27\lib\site-packages\django\utils\_os.py", line 77, in safe_join
    'path component (%s)' % (final_path, base_path))
ValueError: The joined path (E:\jquery.js) is located outside of the base path component (E:\Projects\Third Party\django
-session-security\test_project\static)

From one of the command prompt you showed earlier, I'm guessing you're using Linux, cygwin, etc but I'm running Windows. Do you think this would make a difference?

Thanks for the link, I'd love to read it and hopefully I'll be able to get around to it lol

@jpic
Member
jpic commented Apr 2, 2013

Try python manage.py test session_security from test_project directory.

On Mon, Apr 1, 2013 at 4:10 PM, yscumc notifications@github.com wrote:

Nope, I ran it within django-session-security as setup.py test. I did get
a lot of errors like the following though:

Traceback (most recent call last):
File "C:\Python27\lib\wsgiref\handlers.py", line 85, in run
self.result = application(self.environ, self.start_response)
File "C:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py", line 73, in call
return super(StaticFilesHandler, self).call(environ, start_response)
File "C:\Python27\lib\site-packages\django\core\handlers\wsgi.py", line 255, in call
response = self.get_response(request)
File "C:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py", line 63, in get_response
return self.serve(request)
File "C:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py", line 56, in serve
return serve(request, self.file_path(request.path), insecure=True)
File "C:\Python27\lib\site-packages\django\contrib\staticfiles\views.py", line 38, in serve
absolute_path = finders.find(normalized_path)
File "C:\Python27\lib\site-packages\django\contrib\staticfiles\finders.py", line 239, in find
result = finder.find(path, all=all)
File "C:\Python27\lib\site-packages\django\contrib\staticfiles\finders.py", line 79, in find
matched_path = self.find_location(root, path, prefix)
File "C:\Python27\lib\site-packages\django\contrib\staticfiles\finders.py", line 96, in find_location
path = safe_join(root, path)
File "C:\Python27\lib\site-packages\django\utils_os.py", line 77, in safe_join
'path component (%s)' % (final_path, base_path))
ValueError: The joined path (E:\jquery.js) is located outside of the base path component (E:\Projects\Third Party\django
-session-security\test_project\static)

From one of the command prompt you showed earlier, I'm guessing you're
using Linux, cygwin, etc but I'm running Windows. Do you think this would
make a difference?

Thanks for the link, I'd love to read it and hopefully I'll be able to get
around to it lol


Reply to this email directly or view it on GitHubhttps://github.com/yourlabs/django-session-security/issues/1#issuecomment-15716623
.

http://yourlabs.org http://blog.yourlabs.org
Customer is king - Le client est roi - El cliente es rey.

@yscumc
yscumc commented Apr 2, 2013

Still got same failure...

E:\Projects\Third Party\django-session-security\test_project>python manage.py test session_security

...

Traceback (most recent call last):
  File "C:\Python27\lib\wsgiref\handlers.py", line 85, in run
    self.result = application(self.environ, self.start_response)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py", line 73, in __call__
    return super(StaticFilesHandler, self).__call__(environ, start_response)
  File "C:\Python27\lib\site-packages\django\core\handlers\wsgi.py", line 255, in __call__
    response = self.get_response(request)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py", line 63, in get_response
    return self.serve(request)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py", line 56, in serve
    return serve(request, self.file_path(request.path), insecure=True)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\views.py", line 38, in serve
    absolute_path = finders.find(normalized_path)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\finders.py", line 239, in find
    result = finder.find(path, all=all)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\finders.py", line 79, in find
    matched_path = self.find_location(root, path, prefix)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\finders.py", line 96, in find_location
    path = safe_join(root, path)
  File "C:\Python27\lib\site-packages\django\utils\_os.py", line 77, in safe_join
    'path component (%s)' % (final_path, base_path))
ValueError: The joined path (E:\jquery.js) is located outside of the base path component (E:\Projects\Third Party\django
-session-security\test_project\static)
F..
======================================================================
FAIL: test_double_hide_warning (session_security.tests.script.ScriptTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 154, in test_double_hide_warning
    self.assertWarningShows(9)
  File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 73, in assertWarningShows
    self.fail('Warning did not make it into DOM')
AssertionError: Warning did not make it into DOM

======================================================================
FAIL: test_double_window_inactivity (session_security.tests.script.ScriptTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 139, in test_double_window_inactivity
    self.assertWarningShows(9)
  File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 73, in assertWarningShows
    self.fail('Warning did not make it into DOM')
AssertionError: Warning did not make it into DOM

======================================================================
FAIL: test_single_hide_warning (session_security.tests.script.ScriptTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 131, in test_single_hide_warning
    self.assertWarningShows(9)
  File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 73, in assertWarningShows
    self.fail('Warning did not make it into DOM')
AssertionError: Warning did not make it into DOM

======================================================================
FAIL: test_single_window_inactivity (session_security.tests.script.ScriptTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 118, in test_single_window_inactivity
    self.assertWarningShows(9)
  File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 73, in assertWarningShows
    self.fail('Warning did not make it into DOM')
AssertionError: Warning did not make it into DOM

----------------------------------------------------------------------
Ran 11 tests in 148.446s

FAILED (failures=4)
Destroying test database for alias 'default'...
@jpic
Member
jpic commented Apr 2, 2013

Interresting, it might be all due to jquery.js not being found.

In the test_project, try this command: ./manage.py findstatic jquery.js

It should output something like this:

Found 'jquery.js' here:
  /home/jpic/env/src/session-security/test_project/static/jquery.js

If it doesn't then that might be the problem to fix windows OS support.

On Tue, Apr 2, 2013 at 3:53 PM, yscumc notifications@github.com wrote:

Still got same failure...

E:\Projects\Third Party\django-session-security\test_project>python manage.py test session_security

...

Traceback (most recent call last):
File "C:\Python27\lib\wsgiref\handlers.py", line 85, in run
self.result = application(self.environ, self.start_response)
File "C:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py", line 73, in call
return super(StaticFilesHandler, self).call(environ, start_response)
File "C:\Python27\lib\site-packages\django\core\handlers\wsgi.py", line 255, in call
response = self.get_response(request)
File "C:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py", line 63, in get_response
return self.serve(request)
File "C:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py", line 56, in serve
return serve(request, self.file_path(request.path), insecure=True)
File "C:\Python27\lib\site-packages\django\contrib\staticfiles\views.py", line 38, in serve
absolute_path = finders.find(normalized_path)
File "C:\Python27\lib\site-packages\django\contrib\staticfiles\finders.py", line 239, in find
result = finder.find(path, all=all)
File "C:\Python27\lib\site-packages\django\contrib\staticfiles\finders.py", line 79, in find
matched_path = self.find_location(root, path, prefix)
File "C:\Python27\lib\site-packages\django\contrib\staticfiles\finders.py", line 96, in find_location
path = safe_join(root, path)
File "C:\Python27\lib\site-packages\django\utils_os.py", line 77, in safe_join
'path component (%s)' % (final_path, base_path))
ValueError: The joined path (E:\jquery.js) is located outside of the base path component (E:\Projects\Third Party\django
-session-security\test_project\static)

F..

FAIL: test_double_hide_warning (session_security.tests.script.ScriptTestCase)

Traceback (most recent call last):
File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 154, in test_double_hide_warning
self.assertWarningShows(9)
File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 73, in assertWarningShows
self.fail('Warning did not make it into DOM')
AssertionError: Warning did not make it into DOM

FAIL: test_double_window_inactivity (session_security.tests.script.ScriptTestCase)

Traceback (most recent call last):
File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 139, in test_double_window_inactivity
self.assertWarningShows(9)
File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 73, in assertWarningShows
self.fail('Warning did not make it into DOM')
AssertionError: Warning did not make it into DOM

FAIL: test_single_hide_warning (session_security.tests.script.ScriptTestCase)

Traceback (most recent call last):
File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 131, in test_single_hide_warning
self.assertWarningShows(9)
File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 73, in assertWarningShows
self.fail('Warning did not make it into DOM')
AssertionError: Warning did not make it into DOM

FAIL: test_single_window_inactivity (session_security.tests.script.ScriptTestCase)

Traceback (most recent call last):
File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 118, in test_single_window_inactivity
self.assertWarningShows(9)
File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 73, in assertWarningShows
self.fail('Warning did not make it into DOM')
AssertionError: Warning did not make it into DOM


Ran 11 tests in 148.446s

FAILED (failures=4)
Destroying test database for alias 'default'...


Reply to this email directly or view it on GitHubhttps://github.com/yourlabs/django-session-security/issues/1#issuecomment-15776185
.

http://yourlabs.org http://blog.yourlabs.org
Customer is king - Le client est roi - El cliente es rey.

@yscumc
yscumc commented Apr 2, 2013

Seems to have found it

E:\Projects\Third Party\django-session-security\test_project>manage.py findstatic jquery.js
Found 'jquery.js' here:
  E:\Projects\Third Party\django-session-security\test_project\static\jquery.js
@jpic
Member
jpic commented Apr 2, 2013

Ok, I have changed the test_project template to use {% static %} instead
of {{ STATIC_URL }}, it might work for you.

On Tue, Apr 2, 2013 at 4:53 PM, yscumc notifications@github.com wrote:

Seems to have found it

E:\Projects\Third Party\django-session-security\test_project>manage.py findstatic jquery.js
Found 'jquery.js' here:
E:\Projects\Third Party\django-session-security\test_project\static\jquery.js


Reply to this email directly or view it on GitHubhttps://github.com/yourlabs/django-session-security/issues/1#issuecomment-15779882
.

http://yourlabs.org http://blog.yourlabs.org
Customer is king - Le client est roi - El cliente es rey.

@yscumc
yscumc commented Apr 2, 2013

How long does the tests normally take? I no longer get the ValueError exceptions, but it seems to be hung on the 3rd or 4th test case (after the 3rd period):


E:\Projects\Third Party\django-session-security\test_project>python manage.py test session_security
Creating test database for alias 'default'...
C:\Python27\lib\site-packages\django\conf\urls\defaults.py:3: DeprecationWarning: django.conf.urls.defaults is deprecate
d; use django.conf.urls instead
  DeprecationWarning)

...Not Found: /favicon.ico
Not Found: /favicon.ico

It's been about 15 mins and there's been no change.

Btw, I'm using Django 1.5.

@jpic
Member
jpic commented Apr 2, 2013

It depends, but ~3 minutes on travis which is pretty slow:
https://travis-ci.org/yourlabs/django-session-security

@yscumc
yscumc commented Apr 2, 2013

Ok it was still running after 2 hours so I closed the browser window and it finally moved on. Here are the results:

E:\Projects\Third Party\django-session-security\test_project>python manage.py test session_security
Creating test database for alias 'default'...
C:\Python27\lib\site-packages\django\conf\urls\defaults.py:3: DeprecationWarning: django.conf.urls.defaults is deprecate
d; use django.conf.urls instead
  DeprecationWarning)

...Not Found: /favicon.ico
Not Found: /favicon.ico
ENot Found: /favicon.ico
.Not Found: /favicon.ico
Not Found: /favicon.ico
----------------------------------------
Exception happened during processing of request from ('127.0.0.1', 59719)
Traceback (most recent call last):
  File "C:\Python27\lib\site-packages\django\test\testcases.py", line 998, in _handle_request_noblock
    self.process_request(request, client_address)
  File "C:\Python27\lib\SocketServer.py", line 310, in process_request
    self.finish_request(request, client_address)
  File "C:\Python27\lib\SocketServer.py", line 323, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "C:\Python27\lib\site-packages\django\core\servers\basehttp.py", line 150, in __init__
    super(WSGIRequestHandler, self).__init__(*args, **kwargs)
  File "C:\Python27\lib\SocketServer.py", line 638, in __init__
    self.handle()
  File "C:\Python27\lib\wsgiref\simple_server.py", line 116, in handle
    self.raw_requestline = self.rfile.readline()
  File "C:\Python27\lib\socket.py", line 447, in readline
    data = self._sock.recv(self._rbufsize)
error: [Errno 10054] An existing connection was forcibly closed by the remote host
----------------------------------------
.Not Found: /favicon.ico
Not Found: /favicon.ico
.Not Found: /favicon.ico
Not Found: /favicon.ico
.Not Found: /favicon.ico
...
======================================================================
ERROR: test_double_dont_show_warning (session_security.tests.script.ScriptTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 143, in test_double_dont_show_warning
    self.new_window()
  File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 30, in new_window
    while self.warning_element() is False:
  File "C:\Python27\lib\site-packages\session_security\tests\script.py", line 42, in warning_element
    '#session_security_warning')[0]
  File "C:\Python27\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 373, in find_elements_by_css_selecto
r
    return self.find_elements(by=By.CSS_SELECTOR, value=css_selector)
  File "C:\Python27\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 688, in find_elements
    {'using': by, 'value': value})['value']
  File "C:\Python27\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 160, in execute
    self.error_handler.check_response(response)
  File "C:\Python27\lib\site-packages\selenium\webdriver\remote\errorhandler.py", line 149, in check_response
    raise exception_class(message, screen, stacktrace)
NoSuchWindowException: Message: u'Window not found. The browser window may have been closed.' ; Stacktrace: Method nsCom
mandProcessor.prototype.execute threw an error in file:///c:/users/yas7005/appdata/local/temp/tmp5x67jx/extensions/fxdri
ver@googlecode.com/components/command_processor.js

----------------------------------------------------------------------
Ran 11 tests in 8308.441s

FAILED (errors=1)
Destroying test database for alias 'default'...

Much better than before, but not sure why that test froze

@jpic
Member
jpic commented Apr 4, 2013

Does it manage to open 2 firefox windows ? "double" tests should spawn a second window.

@yscumc
yscumc commented Apr 4, 2013

Yes I think there's one point where there's more than one window open. However, I already had FF open when I was running the tests and perhaps this situation affects the tests. I thought I saw some commands the first time I ran that test that looked as if a new FF profile was being created but it doesn't seem to be the case looking at Process Explorer. I have too many tabs open and don't want to close them yet but I guess I should try it again sometime when I have no tabs open.

@jpic
Member
jpic commented Apr 4, 2013

Selenium should create a new profile per test, so your current session
should not affect it.

On Thu, Apr 4, 2013 at 3:58 PM, yscumc notifications@github.com wrote:

Yes I think there's one point where there's more than one window open.
However, I already had FF open when I was running the tests and perhaps
this situation affects the tests. I thought I saw some commands the first
time I ran that test that looked as if a new FF profile was being created
but it doesn't seem to be the case looking at Process Explorer. I have too
many tabs open and don't want to close them yet but I guess I should try it
again sometime when I have no tabs open.


Reply to this email directly or view it on GitHubhttps://github.com/yourlabs/django-session-security/issues/1#issuecomment-15898521
.

http://yourlabs.org http://blog.yourlabs.org
Customer is king - Le client est roi - El cliente es rey.

@yscumc
yscumc commented Apr 4, 2013

Ok I guess that wasn't the issue then.

It doesn't bother me that the test failed though as long as it works normally so I'll just leave it as is unless you want to look into it and want more info.

@jpic
Member
jpic commented Apr 4, 2013

I have nothing against windows, but it doesn't like me very much so I try
to avoid it ;)

On Thu, Apr 4, 2013 at 4:46 PM, yscumc notifications@github.com wrote:

Ok I guess that wasn't the issue then.

It doesn't bother me that the test failed though as long as it works
normally so I'll just leave it as is unless you want to look into it and
want more info.


Reply to this email directly or view it on GitHubhttps://github.com/yourlabs/django-session-security/issues/1#issuecomment-15901561
.

http://yourlabs.org http://blog.yourlabs.org
Customer is king - Le client est roi - El cliente es rey.

@yscumc
yscumc commented Apr 4, 2013

Haha understood

@jpic
Member
jpic commented Oct 30, 2014

Did you try again with recent selenium, firefox and django updates ?

Maybe tests will run better on windows nowadays ...

@jpic
Member
jpic commented Feb 6, 2015

Hi @yscumc,

Did you find a solution ?

Thanks

@yscumc
yscumc commented Feb 10, 2015

Thanks for the follow up. Unfortunately I haven't found a real solution for the actual problem. I did find a solution for our specific case though; after the programmer who wrote the long running request left, I just modified it to poll at an interval instead lol.

Regarding the testing with selenium, sorry I forgot to reply. I meant to try it again and get back to you but I haven't gotten around to it yet and I'm not sure there would be time to in the near future.

@jpic
Member
jpic commented Feb 11, 2015

Did you try just calling sessionSecurity.activity() in js as much as needed ? Maybe with a recursive setTimeout() callback or something.

It worked for #38

@yscumc
yscumc commented Feb 11, 2015

Nope I haven't, but I doubt it would work in this case. If I remember correctly, the problem was due to Django persisting the session data to the db at the END of the request. The long running request, which is started early, will overwrite the session data from the later requests, even if the later requests indicate activity.

In #38, the user doesn't move the mouse because the main interaction with the page is listening to the audio. Therefore, no activity indication was sent to the server.

In this case, the user actually moves the mouse during the long running requests and sends off activity data to the server. However, when the long running request returns, it overwrites all the previous session data, leading the next request to trigger the logout response.

Unfortunately, I'm not sure there can be a fix for this, as it's an inherent behavior of how Django handles the session data. If there's a way to flush the session data periodically during the long running request, that should fix the problem.

@jpic
Member
jpic commented Feb 11, 2015

On Wed, Feb 11, 2015 at 5:05 PM, yscumc notifications@github.com wrote:

Nope I haven't, but I doubt it would work in this case. If I remember
correctly, the problem was due to Django persisting the session data to the
db at the END of the request. The long running request, which is started
early, will overwrite the session data from the later requests, even if the
later requests indicate activity.

Wouldn't you add the long running request URL to PASSIVE_URLS to avoid it
updating the user's last activity in that case ?

http://yourlabs.org http://blog.yourlabs.org
Customer is king - Le client est roi - El cliente es rey.

@yscumc
yscumc commented Feb 11, 2015

I don't think this would work either, though I'm less certain this time.

Django writes all the session data encoding in a single string as session_data in the django_session table. Even if the session_security's session data is not updated in the long running request, the rest of the session data will be. That data won't include the latest session_security data containing the user's activity and it will be re-encoded as a string back into the django_session table. The next request will decode this session_data and it won't include the latest session_security activity either.

However, this is all conjectures so maybe it'll behave differently?

@jpic
Member
jpic commented Feb 12, 2015

I was thinking:

  1. long polling to PASSIVE_URLS, to prevent any accuracy you have described
    and
  2. call sessionSecurity.activity() in JS meanwhile, to trigger update of
    last session when necessary

Would that work ?

On Wed, Feb 11, 2015 at 5:40 PM, yscumc notifications@github.com wrote:

I don't think this would work either, though I'm less certain this time.

Django writes all the session data encoding in a single string as
session_data in the django_session table. Even if the session_security's
session data is not updated in the long running request, the rest of the
session data will be. That data won't include the latest session_security
data containing the user's activity and it will be re-encoded as a string
back into the django_session table. The next request will decode this
session_data and it won't include the latest session_security activity
either.

However, this is all conjectures so maybe it'll behave differently?


Reply to this email directly or view it on GitHub
#1 (comment)
.

http://yourlabs.org http://blog.yourlabs.org
Customer is king - Le client est roi - El cliente es rey.

@jpic
Member
jpic commented Feb 14, 2015

It's alright if you can't test it, as long as this proposal looks good enough for you then we can close this issue ;)

@yscumc
yscumc commented Feb 15, 2015

Unfortunately, I don't think that would work. Sorry if I wasn't clear in my explanation before but I have interpreted it as you have said and based on what I've seen and tried previously, I do not believe it would work. From my understanding, Django writes the entire encoded session dict to the database at the end of the request, regardless of whether you have manually changed the last activity information in that request or not. PASSIVE_URL only prevent session_security from processing the request, but it does not prevent the regular Django session handling. Therefore, session_security's last activity information will get overwritten with stale data at the end of the long request, because the entire session data is overwritten.

Now I have not tested this hypothesis directly, so I am not 100% certain this is the case, but at the time I encountered this problem, I did a bunch of testing on this issue and this was my conclusion. It seems to be an inherent problem in the way Django handles the saving of session data and the only way I can think of to fix this is to somehow tell Django to update the session data to/from the database periodically before the end of the long request so that it will share the data more nicely with other concurrent requests. I believe the root of this issue is actually a concurrency problem.

I don't have any qualms about this issue being closed as I have worked around it, but it could still be an issue for others. But from the lack of activity on this issue, it looks like this is a rare problem that no one else encountered. It would probably be a good idea to document this known problem somewhere if you wish to close this issue.

@yscumc
yscumc commented Feb 15, 2015

I just did a quick search and it looks like this issue is known by Django:

#10760 Some session data gets lost between multiple concurrent request
Django session cookie lost because of multiple concurrent requests
Tricky issue with django sessions: sometimes session information is erased

Unfortunately, it seems there's not much love on this issue since it doesn't affect most people :(

@jpic
Member
jpic commented Aug 17, 2016

Perhaps #75 fixes this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment