django session post save called twice - python

New to Django Session,
I want to prevent multiple login from same credential.
class UserManager(models.Model):
"""
It hold session_key used for log-in for particular user.
"""
user = models.OneToOneField(User, db_index=True)
session = models.OneToOneField(
Session, null=True, on_delete=models.SET_NULL
)
def session_post_save(**kwargs):
session = kwargs['instance']
uid = session.get_decoded().get('_auth_user_id')
if uid:
profile = UserManager.objects.get(user__id=uid)
# delete old session
if profile.session:
profile.session.delete()
# update session value
profile.session = session
profile.save()
post_save.connect(session_post_save, sender=Session,
dispatch_uid='session_post_save_add_usermanager')
The problem is, I am not able to figure out why session_post_save method getting called twice? or working of session?
First time it get called when user login() method exceuted and having session.get_decoded().get('_auth_user_id') is None and then Session get deleted in some function django/contrib/sessions/backends/base.py(279)cycle_key(). Don't know why?
Again inserted back.
Second time session_post_save method get called while sending response back, session.get_decoded().get('_auth_user_id') is not None.
Thanks

Here's another question with some suggestions for IP addresses about how you might make this happen
detect multiple logins into a Django web application

Related

Django preventing multiple user logins

I wanted to limit users to one login per account. I read numerous posts, and I've been following this post in particular: How to prevent multiple login in Django.
In myapp/models.py, I have this:
from django.conf import settings
from django.db import models
from django.contrib.sessions.models import Session
...
class UserSession(models.Model):
user_acc = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
session_manager = models.OneToOneField(Session, on_delete=models.CASCADE)
In myaurth/views.py, I import from myapp/models.py and have this:
from django.contrib.auth import user_logged_in
from django.dispatch.dispatcher import receiver
...
#receiver(user_logged_in)
def remove_other_sessions(sender, user, request, **kwargs):
# remove other sessions
Session.objects.filter(usersession__user_acc=user).delete()
# save current session
request.session.save()
# create a link from the user to the current session (for later removal)
UserSession.objects.get_or_create(
user_acc=user,
session_manager=Session.objects.get(pk=request.session.session_key)
)
I am not using a custom user model. When I run this code, it works properly when the user logs in the first time and creates an entry in the usersession table. After that I have problems:
If I login a second user, it logs them in, but it replaces the first user's session info with the second user's in the usersession table. I never see more than one entry in the usersession table, which is the last user to successfully login.
Worse, if I attempt to login the first user a second time (and this works even if there is no other user logged in). It removes the entry in the usersession table, but fails with an Update Error:
UpdateError at /
No exception message supplied
Once the entry has been deleted from the usersession table (as in 2 above), a user that is logged in can login to a new session...
I think it is the request.session.save() call that is causing the update error, but I'm at a loss, and I don't understand why I only get one entry in the usersession table when multiple accounts are successfully logged in. My understanding of how it should work is that the second login should successfully login, but the first login should be kicked out.
Any suggestions on what I am doing wrong, would be greatly appreciated.
Thanks--
Al

Django - How to differentiate between users in the database?

I'm building a Django server for my company and I'm still unfamiliar with some processes. I'm sure this is super simple, I'm just completely unaware of how this works.
How do I differentiate between user's data so it doesn't get mixed up?
If Jill is a user and she requests a page of her profile data, how do I not send her Jack's profile data, especially if there are multiple models invovled?
For example, the code in the view would look like this:
def display_profile(request)
profile = Profile.objects.get(???) # What do I put in here?
I understand that I can do:
def display_profile(request, user)
profile = Profile.objects.get(user_id=user)
But that's not my design intention.
Thank you in advance.
As documented
Django uses sessions and middleware to hook the authentication system into request objects.
These provide a request.user attribute on every request which
represents the current user. If the current user has not logged in,
this attribute will be set to an instance of AnonymousUser, otherwise
it will be an instance of User.
So in your case (notice field not being called user_id )
profile = Profile.objects.get(user=user)
In your Django view, you can access the current user with request.user.
So if you want to get a Profile instance matching your current logged in user, just do a query as follow:
profile = Profile.objects.get(user=request.user)
This assumes you have a user foreign key field (or OneToOne) in your Profile model.

Sometimes request.session.session_key is None

I hit a problem when get session_key from request.session.
I am using Django1.8 and Python2.7.10 to set up a RESTful service.
Here is snippet of my login view:
user = authenticate(username=userName, password=passWord)
if user is not None:
# the password verified for the user
if user.is_active:
# app_logger.debug("User is valid, active and authenticated")
if hasattr(user, 'parent') :
login(request, user)
request.session['ut'] = 4
# user type 1 means admin, 2 for teacher, 3 for student, 4 for parents
request.session['uid'] = user.id
description = request.POST.get('description','')
request.session['realname'] = user.parent.realname
request.session['pid'] = user.parent.id
devicemanage.update_user_device(devicetoken, user.id, ostype, description)
children = parentmanage.get_children_info(user.parent.id)
session_id = request.session.session_key
user.parent.login_status = True
user.parent.save()
return JsonResponse({'retcode': 0,'notify_setting':{'receive_notify':user.parent.receive_notify,'notify_with_sound':user.parent.notify_with_sound,'notify_sound':user.parent.notify_sound,'notify_shake':user.parent.notify_shake},'pid':user.parent.id,'children':children,'name':user.parent.realname,'sessionid':session_id,'avatar':user.parent.avatar,'coins':user.parent.coins})
Now when this function is called, I see sometimes session_id is None within the response.
So, after debugging (I set breakpoint at the return JsonResponse(...) line), I see that when I hit the breakpoint the request.session._session_key is None, but request.session.session_key is u'j4lhxe8lvk7j4v5cmkfzytyn5235chf1' and session_id is also None.
Does anyone know how can this happen? Why isn't the value of session_key set when assigning it to session_id before returning the response?
According to John's suggestion.
I fixed the problem by this snippet:
if not request.session.session_key:
request.session.save()
session_id = request.session.session_key
As per documentation:
SessionStore.create() is designed to create a new session (i.e. one
not loaded from the session store and with session_key=None). save()
is designed to save an existing session (i.e. one loaded from the
session store). Calling save() on a new session may also work but has
a small chance of generating a session_key that collides with an
existing one. create() calls save() and loops until an unused
session_key is generated.
Means it is safer to use create() instead of save(). So you can try like this:
if not request.session.session_key:
request.session.create()
session_id = request.session.session_key

How to disconnect update_last_login?

I implemented my own User class from scratch in Django. But when I log in I have this error:
The following fields do not exist in this model or are m2m fields: last_login
I really don't want the field last_login.
I do some reasearch and the problem is here: contrib.aut.models.py
def update_last_login(sender, user, **kwargs):
"""
A signal receiver which updates the last_login date for
the user logging in.
"""
user.last_login = timezone.now()
user.save(update_fields=['last_login'])
user_logged_in.connect(update_last_login)
I found a workaround but it's not an ellegant solution. I added user_logged_in.disconnect(update_last_login) in my models.py file, where my User class is defined.
Is there any better solution for this?
Not sure if this is related to a newer version of django or what, but in my case
user_logged_in.disconnect(update_last_login)
didn't work. This is what works for me (django 2.1):
user_logged_in.disconnect(update_last_login, dispatch_uid='update_last_login')
Currently in Django 1.7...
I think the workaround you defined is the only valid solution (besides from a monkey patch) currently when using the Django auth login() method. I'm just going to assume you are using the standard login() method which is raising this exception.
If we take a look at the source for the login method, we find at the end of the method, a call to execute user_logged_in.send(sender=user.__class__, request=request, user=user). We can't prevent this signal from executing besides from disconnecting it as you have pointed out.
Alternatively, we could monkey patch the login() method to remove that signal call.
from django.contrib.auth import login
def monkey_patch_login(request, user):
"""
Persist a user id and a backend in the request. This way a user doesn't
have to reauthenticate on every request. Note that data set during
the anonymous session is retained when the user logs in.
"""
session_auth_hash = ''
if user is None:
user = request.user
if hasattr(user, 'get_session_auth_hash'):
session_auth_hash = user.get_session_auth_hash()
if SESSION_KEY in request.session:
if _get_user_session_key(request) != user.pk or (
session_auth_hash and
request.session.get(HASH_SESSION_KEY) != session_auth_hash):
# To avoid reusing another user's session, create a new, empty
# session if the existing session corresponds to a different
# authenticated user.
request.session.flush()
else:
request.session.cycle_key()
request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)
request.session[BACKEND_SESSION_KEY] = user.backend
request.session[HASH_SESSION_KEY] = session_auth_hash
if hasattr(request, 'user'):
request.user = user
rotate_token(request)
login = monkey_patch_login
We would put the monkey patch code at the top of the file that needs to call the login() method.

django one session per user

based on docs ( http://docs.djangoproject.com/en/1.1/topics/http/sessions/ ) (yes - 1.1) Django creates unique sessions to all users. Logged user contains _auth_user_id. How can i achieve such check in login:
If new_login._auth_user_id in database:
delete(sessions_containing_same_id_except_new_one)
The main idea is to allow only one session per user and delete old
sessions.
UPDATE: The idea right now is to save sessionid while logging and if sessionid changes delete old entry before replacing. ATM missing part is to get that session id.
UPDATE: I got the sessionid with request.session.session_key. The problem was that sessionid is created after login. If you request key before it was created - it creates new one instead of giving any warning.
I created extra field for user (userattributes extends user):
class UserAttributes(User):
last_session_key = models.CharField(blank=True, null=True, max_length=40)
and method:
def set_session_key(self, key):
if self.last_session_key and not self.last_session_key == key:
Session.objects.get(session_key=self.last_session_key).delete()
self.last_session_key = key
self.save()
and i called it just after login:
auth.login(request, user)
user.userattributes.set_session_key(request.session.session_key)

Categories