User session tracking #93587

Merged
Oleg-Komarov merged 18 commits from user-session into main 2024-08-02 16:04:09 +02:00
6 changed files with 101 additions and 2 deletions
Showing only changes of commit 1a98730e3a - Show all commits

13
bid_main/middleware.py Normal file
View File

@ -0,0 +1,13 @@
from bid_main.models import UserSession
def user_session_middleware(get_response):
def middleware(request):
if (
hasattr(request, 'session')
and hasattr(request, 'user')
and request.user.is_authenticated
):
UserSession.update_or_create_from_request(request)
return get_response(request)
return middleware

View File

@ -0,0 +1,43 @@
# Generated by Django 4.2.13 on 2024-07-30 13:57
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('sessions', '0001_initial'),
('bid_main', '0045_alter_oauth2accesstoken_user_and_more'),
]
operations = [
migrations.CreateModel(
name='UserSession',
fields=[
('id', models.BigAutoField(primary_key=True, serialize=False)),
('agent_is_trusted', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('last_active_at', models.DateTimeField()),
('ip', models.GenericIPAddressField(blank=True, null=True)),
('user_agent', models.CharField(blank=True, max_length=255)),
(
'session',
models.OneToOneField(
editable=False,
on_delete=django.db.models.deletion.CASCADE,
to='sessions.session',
),
),
(
'user',
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name='sessions',
to=settings.AUTH_USER_MODEL,
),
),
],
),
]

View File

@ -8,6 +8,7 @@ from django import urls
from django.conf import settings from django.conf import settings
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.contrib.auth.models import PermissionsMixin from django.contrib.auth.models import PermissionsMixin
from django.contrib.sessions.models import Session
from django.core import validators from django.core import validators
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.mail import send_mail from django.core.mail import send_mail
@ -17,11 +18,12 @@ from django.templatetags.static import static
from django.utils import timezone from django.utils import timezone
from django.utils.deconstruct import deconstructible from django.utils.deconstruct import deconstructible
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
import oauth2_provider.models as oa2_models import oauth2_provider.models as oa2_models
from . import fields from . import fields
from . import hashers from . import hashers
import bid_main.file_utils import bid_main.file_utils
import bid_main.utils
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
nickname_illegal_chars = re.compile(r"[^\w.+-]") nickname_illegal_chars = re.compile(r"[^\w.+-]")
@ -646,3 +648,39 @@ class UserNote(models.Model):
def __str__(self): def __str__(self):
return "Note" return "Note"
class UserSession(models.Model):
id = models.BigAutoField(primary_key=True)
agent_is_trusted = models.BooleanField(default=False, null=False)
created_at = models.DateTimeField(auto_now_add=True)
last_active_at = models.DateTimeField()
ip = models.GenericIPAddressField(null=True, blank=True)
session = models.OneToOneField(Session, on_delete=models.CASCADE, editable=False)
user = models.ForeignKey(User, related_name="sessions", on_delete=models.CASCADE)
user_agent = models.CharField(
max_length=255,
blank=True,
null=False,
)
@classmethod
@transaction.atomic
def update_or_create_from_request(cls, request, user=None):
if not user:
user = request.user
if not request.session.session_key:
request.session.save()
return cls.objects.update_or_create(
session_id=request.session.session_key,
defaults={
'agent_is_trusted': hasattr(request, 'agent') and request.agent.is_trusted,
'ip': bid_main.utils.get_client_ip(request),
'user': user,
'last_active_at': timezone.now(),
'user_agent': request.headers.get("User-Agent", "")[:255],
},
)
def __str__(self):
return f'UserSession pk={self.pk} for {self.user}'

View File

@ -19,7 +19,7 @@ def log_exception(sender, **kwargs):
@receiver(user_logged_in) @receiver(user_logged_in)
def update_user_for_login(sender, request, user, **kwargs): def process_new_login(sender, request, user, **kwargs):
"""Updates user fields upon login. """Updates user fields upon login.
Only saves specific fields, so that the webhook trigger knows what changed. Only saves specific fields, so that the webhook trigger knows what changed.
@ -37,6 +37,7 @@ def update_user_for_login(sender, request, user, **kwargs):
fields.update({"last_login_ip", "current_login_ip"}) fields.update({"last_login_ip", "current_login_ip"})
user.save(update_fields=fields) user.save(update_fields=fields)
models.UserSession.update_or_create_from_request(request, user)
Oleg-Komarov marked this conversation as resolved
Review

should be a call of a task

should be a call of a task
@receiver(m2m_changed) @receiver(m2m_changed)

View File

@ -64,6 +64,7 @@ INSTALLED_APPS = [
"bid_api", "bid_api",
"bid_addon_support", "bid_addon_support",
"background_task", "background_task",
"django_agent_trust",
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -72,6 +73,8 @@ MIDDLEWARE = [
"django.middleware.common.CommonMiddleware", "django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware", "django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware",
"django_agent_trust.middleware.AgentMiddleware",
"bid_main.middleware.user_session_middleware",
"django.contrib.messages.middleware.MessageMiddleware", "django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware",
"oauth2_provider.middleware.OAuth2TokenMiddleware", "oauth2_provider.middleware.OAuth2TokenMiddleware",

View File

@ -10,6 +10,7 @@ csscompressor==0.9.5 ; python_version >= "3.8" and python_version < "4"
deprecated==1.2.14 ; python_version >= "3.8" and python_version < "4" deprecated==1.2.14 ; python_version >= "3.8" and python_version < "4"
dj-database-url==2.2.0 dj-database-url==2.2.0
django-admin-select2==1.0.1 ; python_version >= "3.8" and python_version < "4" django-admin-select2==1.0.1 ; python_version >= "3.8" and python_version < "4"
django-agent-trust==1.1.0
django-background-tasks-updated @ git+https://projects.blender.org/infrastructure/django-background-tasks.git@1.2.10 django-background-tasks-updated @ git+https://projects.blender.org/infrastructure/django-background-tasks.git@1.2.10
django-compat==1.0.15 ; python_version >= "3.8" and python_version < "4" django-compat==1.0.15 ; python_version >= "3.8" and python_version < "4"
django-loginas==0.3.11 ; python_version >= "3.8" and python_version < "4" django-loginas==0.3.11 ; python_version >= "3.8" and python_version < "4"