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

1"""Functions for magic login.""" 

2 

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 _ 

8 

9import sesame.utils 

10import structlog 

11from rest_framework import serializers 

12from rest_framework.generics import GenericAPIView 

13from rest_framework.response import Response 

14 

15import InvenTree.version 

16from InvenTree.helpers_email import send_email 

17 

18logger = structlog.get_logger('inventree') 

19 

20 

21def send_simple_login_email(user, link): 

22 """Send an email with the login link to this user.""" 

23 site_name = InvenTree.version.inventreeInstanceName() 

24 

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 ) 

29 

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 ) 

36 

37 

38class GetSimpleLoginSerializer(serializers.Serializer): 

39 """Serializer for the simple login view.""" 

40 

41 email = serializers.CharField(label=_('Email')) 

42 

43 

44class GetSimpleLoginView(GenericAPIView): 

45 """View to send a simple login link.""" 

46 

47 permission_classes = () 

48 serializer_class = GetSimpleLoginSerializer 

49 

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'}) 

56 

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) 

65 

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 

72 

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