Actually, the right way to get your viewpoint heard is to take the
matter to the django-developers mailing list, where topics related to
Django's development are discussed. You'll have more luck posting
suggestions and criticism there than here or on the ticket tracker.
However, please keep in mind that we're currently running up to Django
1.1, so it's likely that anything that's not an outright bug might be
left by the wayside while we close bugs for the final release. If you
don't get an immediate response, be patient and wait until a bit after
the release when we all have a bit more time.
Jacob
Cheers.
DISCLAIMER: Careful! I haven't tested this! A Timer object uses a
thread internally. Be careful with synchronization and related stuff.
HTH.
Cheers,
Paulo Köch
Both of these solutions have the same showstopping flaw: There's no way,
on the server side, to associate a timed-out user with their previous
session. Because the web browser doesn't send the session cookie. This
whole "request for an enhancement" is based on wanting Django to act on
information it cannot possibly know about in the first place and your
solution suffers from the same problem.
The only way to work around something like this is to have "never
expire" sessions and manually manage the timing out on the server side.
Regards,
Malcolm
#Pseudo code. Not tested nor proven.
class TimeoutTimerPool(object):
"""A pool that tracks a set of users and their active session.
Tipically used as a singleton."""
from threading import Timer
def __init__(self):
self.pool = {}
def expiration_handler(self, session_id, user_id):
#Take action with session_id and/or user_id here
pass
def register_user_presence(self, session_id, user_id):
"""Tracks user activity/presence. No magic mojo here, must be
called manually (or integrated in some middleware)."""
# Timers can be reset. So, we just dispose the current and create another.
self.unregister_session(session_id)
self.pool[session_id] = Timer(SESSION_TIMEOUT_OR_SOMETHING,
self.expiration_handler, session_id, user_id)
self.pool[session_id].start()
def untrack_session(self, session_id):
"""Forfeits user tracking. For logouts and such."""
if self.pool.haskey(session_id):
current_timer = self.pool[session_id]
if current_timer.isAlive():
current_time.cancel()
# Don't kow if this is the right way to dispose a Timer
del self.pool[session_id]
Cheers,
Paulo Köch
Cheers,
Paulo Köch
I'm hesitant to rain on your parade here, as you've put some effort into
writing a bunch of code. But the approach is flawed. As soon as you have
multiple processes involved (which is going to be normal), this
in-memory storage won't work. There's no guarantee that the same session
will go to the same process (not to mention that the processes will stop
and start over time).
If you're going to do something like this, it has to be external from
the web-process lifecycle and a centralised point that each process
talks to. Some kind of server daemon.
At that point it becomes equivalent to just looking in the sessions
table for sessions that have already expired (which is a simple
queryset). (the server daemon in question is the database server, so the
pattern holds).
I also don't think you're solving the original problem here (detecting
when a user who is coming back has an expired session, rather than never
been logged in) and detecting when sessions have expired can already be
done by looking at the database table.
Regards,
Malcolm
Doh, missed that! This would only work in a worker model, restricting
deploy strategies. You're 100% correct. Some elaborate solution would
be required.
> At that point it becomes equivalent to just looking in the sessions
> table for sessions that have already expired (which is a simple
> queryset). (the server daemon in question is the database server, so the
> pattern holds).
>
> I also don't think you're solving the original problem here (detecting
> when a user who is coming back has an expired session, rather than never
> been logged in) and detecting when sessions have expired can already be
> done by looking at the database table.
Based on this, I can't see why a simple custom cron job inspecting the
pickled session data (assuming the user_id is in the session) before
purging old session would not suffice. Care to elaborate?
Cheers.
I think you've arrived back at the point of what Django already
provides, which is why the original ticket was closed.
You run "django-admin.py cleanup" in a cronjob and all the old sessions
are removed. You don't need to check the user ids or anything like that.
Expired sessions are expired, no matter who they belonged to.
Calling logout(), as the original poster requested doesn't achieve
anything (it does nothing). If it did do something, it would still be a
bad idea to call it, because the user could have already logged in again
and logging them out would be unfortunate.
So, yes, the cronjob is the solution. Which is why it's been in Django
for the last four years. :-)
Regards,
Malcolm
Doesn't this generate a new session_id?
Cheers,
Paulo Köch
More importantly it sets the user's status to be logged out. If they had
logged in again since their previous session expired, you have now just
logged them out again. In the web business we call that "not friendly".
Malcolm
> As soon as the session expires, Django
> updates the records in the django.sessions table. As such, the only
> unique identifier for the user, "session_key", is overwritten.
This isn't what happens when a session expires at all. Django doesn't
know that the session has expired because all it sees is a user arriving
without a session cookie (the cookie has expired, so the browser doesn't
send it). Thus, the user looks like a brand new user at that point.
So far this fact (that the cookie is not sent) has been mentioned three
or four times in this thread. You really have to start believing us.
> If there is some straightforward way to intercept the call to update
> that table at the time of the automated logout, I'm all ears.
There is no automated logout, either. There is only explicit logout
prior to a session being expired.
Regards,
Malcolm
The semantics are very important. We've been discussing the problem from
the server side where there is no concept of automatic logout. Using the
wrong words confuses things and there was enough confusion when you
started this thread that being precise is necessary. We aren't
discussing what the user sees here and there's never been any
misunderstanding as to what problem you've been trying to solve.
> 1. User access Django-based website.
> 2. Django generates a session cookie with an expiration date based
> upon SESSION_COOKIE_AGE. (In this example, it's set to 3600)
> 3. User logs in
> 4. User traverses website for one hour (3600 seconds)
> 5. Browser removes expired cookie
> 6. User attempts to click new link in Django-based website
> 7. Django detects the missing cookie
> 8. Django redirects user to login page
> 9. Django generates session cookie, inserts a new record into
> django.sessions, and leaves old session information in django.sessions
> table
Yes, this is correct and, yes, it's impossible to do anything special at
step 7 because there's absolutely no difference between the expired
session and the new user or somebody who hasn't visited for the past two
years.
Regards,
Malcolm
I think this is where you're getting hung up. Django doesn't "detect"
a "missing" cookie; Django sees a request from a browser that doesn't
include a cookie. Nothing's missing; it's just a new browser without a
cookie.
To Django, there's *no difference* between a user browsing a site
until his cookie expires then coming back, and the same user browsing
until the cookie expires and then a *second* user visiting without a
cookie. There's no "I used to have a cookie but now I don't" header;
there's either a session cookie, or there isn't.
Huuuze, I can appreciate that this is an infuriating aspect of HTTP --
statelessness is a real bitch sometimes. But you need to accept that
you're asking the impossible here and move on.
Jacob