This repository has been archived on 2026-03-30. You can view files and clone it, but cannot push or open issues or pull requests.
RepApp/repapp/one_time_login/utils.py

121 lines
3.6 KiB
Python

import datetime
import random
import logging
from hashlib import sha256
from django.conf import settings
from django.contrib import messages
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _
from django.utils import translation
from django.urls import reverse_lazy
from django.utils.timezone import now
from django.contrib.auth import authenticate
from .models import OneTimeLogin
logger = logging.getLogger(__name__)
def create_one_time_login(user, url) -> OneTimeLogin:
"""
create_one_time_login creates a one time login object for guest user logins.
"""
secret = sha256(
f'{user.email}{url}{datetime.datetime.now()}{random.randint(0,9999999)}'.encode(
'utf-8')
).hexdigest()
otl = OneTimeLogin(
secret=secret,
user=user,
url=url,
)
otl.save()
logger.debug('One time login created for user %s and URL %s', user, url)
return otl
def send_one_time_login_mail(secret, mail, request, language=None):
"""
Send a mail with a one time login link.
"""
if language is None:
language = settings.LANGUAGE_CODE
url = request.build_absolute_uri(reverse_lazy(
'one_time_login:login', kwargs={'secret': secret}))
cur_language = translation.get_language()
try:
translation.activate(language)
subject = render_to_string('one_time_login/mail_subject.txt', {
'organization': settings.ORGANIZATION,
}).replace('\n', '')
html = render_to_string('one_time_login/mail_content.html', {
'link': url,
'organization': settings.ORGANIZATION,
})
text = render_to_string('one_time_login/mail_content.html', {
'link': url,
'organization': settings.ORGANIZATION,
})
finally:
translation.activate(cur_language)
send_ok = send_mail(
subject=subject,
message=text,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[mail],
fail_silently=True,
html_message=html
)
logger.debug('One time login mail sent to mail %s', mail)
if send_ok > 0:
messages.add_message(request, messages.INFO, _(
'A new login link was sent by mail.'))
else:
messages.add_message(request, messages.ERROR, _(
'Sending of one time login link failed!'))
def one_time_login(request, secret):
"""
Evaluates a one time login request.
"""
try:
otl = OneTimeLogin.objects.get(secret=secret)
except OneTimeLogin.DoesNotExist:
logger.info('No one time login for secret %s', secret)
return (None, None)
if otl.login_used:
messages.add_message(request, messages.ERROR,
_('Login was used already.'))
logger.info('One time login with secret %s for user %s and URL %s was already used',
secret, otl.user, otl.url)
if otl.user.email:
logger.debug(
'Creating and sending one time login for user %s and URL %s', otl.user, otl.url)
# create and send a new one time login
new_secret = create_one_time_login(otl.user, otl.url)
send_one_time_login_mail(new_secret, otl.user.email, request)
return (None, None)
else:
otl.login_used = True
otl.login_date = now()
otl.save()
user = authenticate(request, username=secret, password=None)
logger.info('Authenticated user %s with one time login.', user)
return (user, otl.url)