User session tracking #93587

Merged
Oleg-Komarov merged 18 commits from user-session into main 2024-08-02 16:04:09 +02:00
Owner

Motivation

A user needs to know how their account is being accessed/used.
At the very minimum, we need to display information about recent sign-ins and active sessions.

This PR adds:

  • a new "Active Sessions" page that lists existing sessions linked to a user, with an option to terminate a particular session;
  • an email that is sent to a confirmed email address when a new login happens from a different IP address.

Implementation

Builtin django sessions are lacking some essential features:

  • it's impossible to efficiently list all sessions belonging to a user
  • there is not enough metadata: when and where a session was created

This PR adds a cross table bid_main_user_session that links to both django_session and bid_main_user tables, and also stores info about sign-in timestamp, last activity timestamp, IP and User-Agent.
A UserSession object is updated (or created, for new sessions and for active sessions existing before the rollout) on every authenticated request to update the last_active_at field.
This is done in a new user_session_middleware.

A further improvement (intentionally excluded from the PR): use geoip2 to display an IP-based location in the Active Sessions listing and the email.

## Motivation A user needs to know how their account is being accessed/used. At the very minimum, we need to display information about recent sign-ins and active sessions. This PR adds: - a new "Active Sessions" page that lists existing sessions linked to a user, with an option to terminate a particular session; - an email that is sent to a confirmed email address when a new login happens from a different IP address. ## Implementation Builtin django sessions are lacking some essential features: - it's impossible to efficiently list all sessions belonging to a user - there is not enough metadata: when and where a session was created This PR adds a cross table `bid_main_user_session` that links to both `django_session` and `bid_main_user` tables, and also stores info about sign-in timestamp, last activity timestamp, IP and User-Agent. A `UserSession` object is updated (or created, for new sessions and for active sessions existing before the rollout) on every authenticated request to update the `last_active_at` field. This is done in a new `user_session_middleware`. A further improvement (intentionally excluded from the PR): use geoip2 to display an IP-based location in the Active Sessions listing and the email.
Oleg-Komarov added 6 commits 2024-08-01 13:14:48 +02:00
Oleg-Komarov added 1 commit 2024-08-01 13:42:48 +02:00
Oleg-Komarov added 1 commit 2024-08-01 14:50:29 +02:00
Oleg-Komarov changed title from WIP: User session tracking to User session tracking 2024-08-01 14:59:55 +02:00
Oleg-Komarov added 1 commit 2024-08-01 15:00:23 +02:00
Anna Sirota reviewed 2024-08-02 11:41:15 +02:00
@ -260,0 +277,4 @@
recipient_list=[email],
fail_silently=False,
)
except (smtplib.SMTPException, OSError):
Owner

might be better to make this a background task instead of wrapping into except.

might be better to make this a background task instead of wrapping into except.
Oleg-Komarov marked this conversation as resolved
@ -649,0 +689,4 @@
def device(self):
if self.user_agent:
return user_agents.parse(self.user_agent)
else:
Owner

superfluous else

superfluous else
Oleg-Komarov marked this conversation as resolved
@ -38,1 +38,3 @@
if user.has_confirmed_email:
try:
email.send_new_user_session(user_session)
Owner

should be a call of a task

should be a call of a task
Oleg-Komarov marked this conversation as resolved
@ -432,0 +448,4 @@
class TerminateSessionView(LoginRequiredMixin, View):
def post(self, request, *args, **kwargs):
if user_session := self.request.user.sessions.filter(pk=kwargs.get('pk', 0)).first():
Owner

getting the pk (pk = kwargs['pk']: it's fine to expect it to be present at this point) and filtering the session on the separate line would make this more readable.

getting the `pk` (`pk = kwargs['pk']`: it's fine to expect it to be present at this point) and filtering the session on the separate line would make this more readable.
Oleg-Komarov marked this conversation as resolved
Oleg-Komarov added 2 commits 2024-08-02 15:34:24 +02:00
Oleg-Komarov added 1 commit 2024-08-02 15:37:35 +02:00
Oleg-Komarov added 1 commit 2024-08-02 15:41:20 +02:00
Oleg-Komarov added 1 commit 2024-08-02 15:43:43 +02:00
Oleg-Komarov added 1 commit 2024-08-02 15:48:37 +02:00
Oleg-Komarov added 1 commit 2024-08-02 15:52:50 +02:00
Oleg-Komarov added 1 commit 2024-08-02 15:55:57 +02:00
Oleg-Komarov added 1 commit 2024-08-02 15:59:41 +02:00
Anna Sirota approved these changes 2024-08-02 16:00:10 +02:00
Anna Sirota left a comment
Owner

LGTM

LGTM
Oleg-Komarov merged commit ce31207f36 into main 2024-08-02 16:04:09 +02:00
Oleg-Komarov deleted branch user-session 2024-08-02 16:04:10 +02:00
Sign in to join this conversation.
No description provided.