Coverage for src/backend/InvenTree/InvenTree/magic_login.py: 77%
45 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
1"""Functions for magic login."""
3from django.conf import settings
4from django.contrib.auth.models import User
5from django.template.loader import render_to_string
6from django.urls import reverse
7from django.utils.translation import gettext_lazy as _
9import sesame.utils
10import structlog
11from rest_framework import serializers
12from rest_framework.generics import GenericAPIView
13from rest_framework.response import Response
15import InvenTree.version
16from InvenTree.helpers_email import send_email
18logger = structlog.get_logger('inventree')
21def send_simple_login_email(user, link):
22 """Send an email with the login link to this user."""
23 site_name = InvenTree.version.inventreeInstanceName()
25 context = {'username': user.username, 'site_name': site_name, 'link': link}
26 email_plaintext_message = render_to_string(
27 'InvenTree/user_simple_login.txt', context
28 )
30 send_email(
31 f'[{site_name}] ' + _('Log in to the app'),
32 email_plaintext_message,
33 [user.email],
34 settings.DEFAULT_FROM_EMAIL,
35 )
38class GetSimpleLoginSerializer(serializers.Serializer):
39 """Serializer for the simple login view."""
41 email = serializers.CharField(label=_('Email'))
44class GetSimpleLoginView(GenericAPIView):
45 """View to send a simple login link."""
47 permission_classes = ()
48 serializer_class = GetSimpleLoginSerializer
50 def post(self, request, *args, **kwargs):
51 """Get the token for the current user or fail."""
52 serializer = self.serializer_class(data=request.data)
53 serializer.is_valid(raise_exception=True)
54 self.email_submitted(email=serializer.data['email'])
55 return Response({'status': 'ok'})
57 def email_submitted(self, email):
58 """Notify user about link."""
59 user = self.get_user(email)
60 if user is None: 60 ↛ 63line 60 didn't jump to line 63 because the condition on line 60 was always true
61 logger.warning('User email not found: %s', email)
62 return
63 link = self.create_link(user)
64 send_simple_login_email(user, link)
66 def get_user(self, email):
67 """Find the user with this email address."""
68 try:
69 return User.objects.get(email=email)
70 except User.DoesNotExist:
71 return None
73 def create_link(self, user):
74 """Create a login link for this user."""
75 link = reverse('sesame-login')
76 link = self.request.build_absolute_uri(link)
77 link += sesame.utils.get_query_string(user)
78 return link