django one session per user - python

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)

Related

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

django session post save called twice

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

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.

Adding a username property to webapp2 User model after entity has been created (with Simpleauth)

I'm currently using Python / App Engine / SimpleAuth to provide OAuth login to my application. The current workflow is that users login with OAuth, and they can later create a unique username for themselves in the app.
I'm having problems creating the unique username after the webapp2 User entity has already been created. I see that in the webapp2 model there is a way to enable a unique username within the application Entity group, but I don't know how to set it for myself. (I'm using SimpleAuth to set everything for other OAuth providers.)
I want to check to see if the user-submitted 'username' exists, and if it doesn't to add it as a property to the currently logged-in user. I'd appreciate any help/pointers on this!
I think you could extend webapp2_extras.appengine.auth.models.User and add username property, e.g.
from webapp2_extras.appengine.auth.models import User as Webapp2User
class User(Webapp2User):
username = ndb.StringProperty(required=True)
Then, to create a webapp2 app you'd need a config which includes this:
APP_CFG = {
'webapp2_extras.auth': {
'user_model': User, # default is webapp2_extras.appengine.auth.models.User
'user_attributes': ['username'] # list of User model properties
}
}
app = webapp2.WSGIApplication(config=APP_CFG)
Havign the above, creating a new user using the following code will ensure username is unique (ensured by Unique model):
auth_id = 'some-auth-id' # e.g. 'google:123456789', see simpleauth example.
ok, props = User.create_user(auth_id, unique_properties=['username'],
username='some-username',
...)
if not ok:
# props list will contain 'username', indicating that
# another entity with the same username already exists
...
Problem is, with this configuration you are bound to set username during the creation time.
If you wanted to make username optional, or let users set/change it later on, you would probably want to change the above code to something like this:
class User(Webapp2User):
username = ndb.StringProperty() # note, there's no required=True
# when creating a new user:
auth_id = 'some-auth-id' # e.g. 'google:123456789', see simpleauth example.
ok, props = User.create_user(auth_id, unique_properties=[], ...)
Basically, unique_properties will be empty list (or you can just skip it). Also, you could temporarily assign username property to something like user.key.id() until the user decides to change their username to something more meaningful. Take, for instance, Google+ profile links: mine is currently https://plus.google.com/114517983826182834234, but if they let me change it, I would try something like https://plus.google.com/+IamNotANumberAnymore
Then, in a "change/set username" form handler, you could check if a username already exists and update User entity (if it doesn't):
def handle_change_username(self):
user = ... # get the user who wants to change their username
username = self.request.get('username')
uniq = 'User.username:%s' % username
ok = User.unique_model.create(uniq)
if ok:
user.username = username
user.put()
else:
# notify them that this username
# is already taken
...
User.unique_model.create(uniq) will create a Unique entity with the given value if it didn't exist. In this case ok will be True. Otherwise, ok will be False which indicates that an entity with that value (a unique username in this case) already exists.
Also, you might want to put User.unique_model.create() and user.put() in the same transaction (it'll be XG because they are in different entity groups).
Hope this helps!

Categories