David542
David542

Reputation: 110093

How to make a django user inactive and invalidate all their sessions

To make a user inactive I usually do:

User.objects.get(pk=X).update(is_active=False)

However, this doesn't log out the user or do anything session-related. Is there a built-in in django to make a user immediately inactive, or what would be the best way to accomplish this?

One similar answer is this: https://stackoverflow.com/a/954318/651174, but that doesn't work great if there are millions of sessions (it's a brute force way iterating over all sessions). Though this is from 2009, so hopefully there's a better way as of today.

Upvotes: 4

Views: 2500

Answers (2)

Greg Ball
Greg Ball

Reputation: 3811

Changing the user's password invalidates all the user's sessions since around Django version 2.2. (This works without scanning the whole session table. An HMAC of the password field is saved on login, and on any request where the request.user is accessed, the login session is treated as no-longer-valid if the current HMAC does not match.)

https://docs.djangoproject.com/en/2.2/topics/auth/default/#session-invalidation-on-password-change-1

user = User.objects.get(pk=user_id)
user.is_active = False
user.set_unusable_password()
user.save()

Upvotes: 3

As mentioned, you can use django-user-session or django-qsessions. But these include some other metadata such as user agent and ip address, and you may not want these for some reason. Then you need to write your custom session backend

I adjusted the example a bit and created one to the my needs.

session_backend.py:

from django.contrib.auth import get_user_model
from django.contrib.sessions.backends.db import SessionStore as DBStore
from django.contrib.sessions.base_session import AbstractBaseSession
from django.db import models

User = get_user_model()


class QuickSession(AbstractBaseSession):
    # Custom session model which stores user foreignkey to asssociate sessions with particular users.
    user = models.ForeignKey(User, null=True, on_delete=models.CASCADE)

    @classmethod
    def get_session_store_class(cls):
        return SessionStore


class SessionStore(DBStore):
    @classmethod
    def get_model_class(cls):
        return QuickSession

    def create_model_instance(self, data):
        obj = super().create_model_instance(data)

        try:
            user_id = int(data.get('_auth_user_id'))
            user = User.objects.get(pk=user_id)
        except (ValueError, TypeError, User.DoesNotExist):
            user = None
        obj.user = user
        return obj

and in settings.py:

SESSION_ENGINE = 'path.to.session_backend'

To delete all session for a user:

from session_backend import QuickSession
QuickSession.objects.filter(user=request.user).delete()

You may write your custom save method for user model to automatically delete all sessions for the user if the is_active field is set to False.

Keep in mind that, user field for those who are not logged in will be NULL.

Upvotes: 3

Related Questions