Preparations for Android app

This commit is contained in:
Kumi 2023-07-08 16:11:38 +02:00
parent 02400d5ce0
commit ae2a6eda79
Signed by: kumi
GPG key ID: ECBCC9082395383F
6 changed files with 76 additions and 0 deletions

View file

@ -10,6 +10,16 @@ from django.shortcuts import resolve_url
from ..models.session import AuthSession
# Somewhat shamelessly copied from django.contrib.auth.views
#
# Original source:
# https://github.com/django/django/blob/main/django/contrib/auth/views.py
#
# License:
# BSD 3-Clause "New" or "Revised" License
# Copyright (c) Django Software Foundation and individual contributors.
# All rights reserved.
# https://github.com/django/django/blob/main/LICENSE
class AuthSessionRequiredMixin(RedirectURLMixin):
redirect_field_name = REDIRECT_FIELD_NAME

View file

@ -10,6 +10,7 @@ from django.shortcuts import resolve_url
from urllib.parse import urlparse
from ..models.otp import TOTPSecret
from ..models.app import AppSession
class TimeoutMixin:
@ -30,6 +31,9 @@ class TimeoutMixin:
elif request.session["LastActivity"] < (timezone.now() - timezone.timedelta(minutes=settings.REVERIFY_AFTER_INACTIVITY_MINUTES)).timestamp():
try:
assert request.user.totpsecret.active
request.session["AppSession"] = AppSession.get_for_user(request.user)
return redirect_to_login(path, resolve_url("auth:reverify"), REDIRECT_FIELD_NAME)
except (AssertionError, TOTPSecret.DoesNotExist):
messages.error(
@ -39,6 +43,7 @@ class TimeoutMixin:
messages.error(
request, "Something went wrong, please try logging in again."
)
logout(request)
else:
request.session["LastActivity"] = timezone.now().timestamp()

View file

@ -0,0 +1,49 @@
from django.db import models
from django.contrib.auth import get_user_model
from uuid import uuid4
from jwt import decode, InvalidTokenError
class AppKey(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
user = models.ForeignKey(get_user_model(), models.CASCADE)
device = models.CharField(max_length=255)
key = models.TextField()
active = models.BooleanField(default=True)
def __str__(self):
return f"{self.user.username} - {self.device}"
def validateJWT(self, jwt):
try:
return decode(jwt, self.key, algorithms=['HS256'])
except InvalidTokenError:
return False
class AppSession(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
user = models.ForeignKey(get_user_model(), models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
used = models.DateTimeField(null=True, blank=True)
approved = models.BooleanField(default=False)
@property
def valid(self):
return self.created > timezone.now() - timezone.timedelta(minutes=5)
@classmethod
def get_for_user(cls, user, create = True):
assert user
if not user.appkey_set.filter(active=True).exists():
return
user_sessions = cls.objects.filter(user=user)
for session in user_sessions:
if session.valid and not session.used:
return session
if create:
return cls.objects.create(user=user)

View file

@ -0,0 +1,2 @@
from django.views.generic import JSONView

View file

@ -4,6 +4,7 @@ from django.http import HttpResponseRedirect
from django.urls import reverse_lazy
from ..forms.otp import TOTPLoginForm
from ..models.app import AppSession
from frontend.mixins.views import TitleMixin
@ -17,4 +18,12 @@ class ReverifyView(TitleMixin, LoginView):
def form_valid(self, form):
self.request.session["LastActivity"] = timezone.now().timestamp()
try:
app_session = AppSession.objects.get(id=self.request.session["AppSession"])
app_session.used = True
app_session.save()
except AppSession.DoesNotExist:
pass
return HttpResponseRedirect(self.get_success_url())

View file

@ -15,6 +15,7 @@ django-crispy-forms
pyqrcode
pypng
django-ajax-datatable
pyjwt
# For MySQL: