Commit 79bb34b9 authored by Erlend Ydse's avatar Erlend Ydse
Browse files

Move RememberMe to users app

parent 5799d4a3
# Generated by Django 3.1 on 2021-04-15 16:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0014_user_symmetric_friends'),
]
operations = [
migrations.CreateModel(
name='RememberMe',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('remember_me', models.CharField(max_length=500)),
],
),
]
...@@ -31,6 +31,18 @@ class User(AbstractUser): ...@@ -31,6 +31,18 @@ class User(AbstractUser):
symmetric_friends = models.ManyToManyField("self", blank=True) symmetric_friends = models.ManyToManyField("self", blank=True)
class RememberMe(models.Model):
"""Django model for an remember_me cookie used for remember me functionality.
Attributes:
remember_me: Value of cookie used for remember me
"""
remember_me = models.CharField(max_length=500)
def __str__(self):
return self.remember_me
def athlete_directory_path(instance, filename): def athlete_directory_path(instance, filename):
""" """
Return the path for an athlete's file Return the path for an athlete's file
......
...@@ -4,7 +4,7 @@ from django import forms ...@@ -4,7 +4,7 @@ from django import forms
from django.contrib.auth import get_user_model, password_validation from django.contrib.auth import get_user_model, password_validation
from rest_framework import serializers from rest_framework import serializers
from users.models import AthleteFile, Offer from users.models import AthleteFile, Offer, RememberMe
class UserSerializer(serializers.HyperlinkedModelSerializer): class UserSerializer(serializers.HyperlinkedModelSerializer):
...@@ -105,6 +105,18 @@ class UserPutSerializer(serializers.ModelSerializer): ...@@ -105,6 +105,18 @@ class UserPutSerializer(serializers.ModelSerializer):
instance.symmetric_friends.add(validated_data["symmetric_friends"][0]) instance.symmetric_friends.add(validated_data["symmetric_friends"][0])
return instance return instance
class RememberMeSerializer(serializers.HyperlinkedModelSerializer):
"""Serializer for an RememberMe. Hyperlinks are used for relationships by default.
Serialized fields: remember_me
Attributes:
remember_me: Value of cookie used for remember me functionality
"""
class Meta:
model = RememberMe
fields = ["remember_me"]
class AthleteFileSerializer(serializers.HyperlinkedModelSerializer): class AthleteFileSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source="owner.username") owner = serializers.ReadOnlyField(source="owner.username")
......
...@@ -16,4 +16,5 @@ urlpatterns = [ ...@@ -16,4 +16,5 @@ urlpatterns = [
views.AthleteFileDetail.as_view(), views.AthleteFileDetail.as_view(),
name="athletefile-detail", name="athletefile-detail",
), ),
path("api/remember_me/", views.RememberMe.as_view(), name="remember_me"),
] ]
import base64
import pickle
from collections import namedtuple
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core.exceptions import PermissionDenied
from django.core.signing import Signer
from django.db.models import Q from django.db.models import Q
from rest_framework import generics, mixins, permissions from rest_framework import generics, mixins, permissions
from rest_framework.parsers import FormParser, MultiPartParser from rest_framework.parsers import FormParser, MultiPartParser
from rest_framework.permissions import IsAuthenticatedOrReadOnly from rest_framework.permissions import IsAuthenticatedOrReadOnly
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken
from users.models import AthleteFile, Offer from users.models import AthleteFile, Offer
from users.permissions import IsAthlete, IsCoach, IsCurrentUser from users.permissions import IsAthlete, IsCoach, IsCurrentUser
from users.serializers import (AthleteFileSerializer, OfferSerializer, from users.serializers import (AthleteFileSerializer, OfferSerializer,
UserGetSerializer, UserPutSerializer, RememberMeSerializer, UserGetSerializer,
UserSerializer) UserPutSerializer, UserSerializer)
from workouts.mixins import CreateListModelMixin from workouts.mixins import CreateListModelMixin
from workouts.parsers import MultipartJsonParser from workouts.parsers import MultipartJsonParser
from workouts.permissions import IsOwner, IsReadOnly from workouts.permissions import IsOwner, IsReadOnly
...@@ -75,6 +83,52 @@ class UserDetail( ...@@ -75,6 +83,52 @@ class UserDetail(
return self.partial_update(request, *args, **kwargs) return self.partial_update(request, *args, **kwargs)
# Allow users to save a persistent session in their browser
class RememberMe(
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView,
):
serializer_class = RememberMeSerializer
def get(self, request):
if request.user.is_authenticated == False:
raise PermissionDenied
else:
return Response({"remember_me": self.rememberme()})
def post(self, request):
cookie_object = namedtuple("Cookies", request.COOKIES.keys())(
*request.COOKIES.values()
)
user = self.get_user(cookie_object)
refresh = RefreshToken.for_user(user)
return Response(
{
"refresh": str(refresh),
"access": str(refresh.access_token),
}
)
def get_user(self, cookie_object):
decode = base64.b64decode(cookie_object.remember_me)
user, sign = pickle.loads(decode)
# Validate signature
if sign == self.sign_user(user):
return user
def rememberme(self):
creds = [self.request.user, self.sign_user(str(self.request.user))]
return base64.b64encode(pickle.dumps(creds))
def sign_user(self, username):
signer = Signer()
signed_user = signer.sign(username)
return signed_user
class OfferList( class OfferList(
mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView
): ):
......
# Generated by Django 3.1 on 2021-04-15 16:40
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('workouts', '0005_auto_20210307_2015'),
]
operations = [
migrations.DeleteModel(
name='RememberMe',
),
]
...@@ -139,16 +139,3 @@ class WorkoutFile(models.Model): ...@@ -139,16 +139,3 @@ class WorkoutFile(models.Model):
get_user_model(), on_delete=models.CASCADE, related_name="workout_files" get_user_model(), on_delete=models.CASCADE, related_name="workout_files"
) )
file = models.FileField(upload_to=workout_directory_path) file = models.FileField(upload_to=workout_directory_path)
class RememberMe(models.Model):
"""Django model for an remember_me cookie used for remember me functionality.
Attributes:
remember_me: Value of cookie used for remember me
"""
remember_me = models.CharField(max_length=500)
def __str__(self):
return self.remember_me
...@@ -3,8 +3,7 @@ ...@@ -3,8 +3,7 @@
from rest_framework import serializers from rest_framework import serializers
from rest_framework.serializers import HyperlinkedRelatedField from rest_framework.serializers import HyperlinkedRelatedField
from workouts.models import (Exercise, ExerciseInstance, RememberMe, Workout, from workouts.models import Exercise, ExerciseInstance, Workout, WorkoutFile
WorkoutFile)
class ExerciseInstanceSerializer(serializers.HyperlinkedModelSerializer): class ExerciseInstanceSerializer(serializers.HyperlinkedModelSerializer):
...@@ -221,17 +220,3 @@ class ExerciseSerializer(serializers.HyperlinkedModelSerializer): ...@@ -221,17 +220,3 @@ class ExerciseSerializer(serializers.HyperlinkedModelSerializer):
class Meta: class Meta:
model = Exercise model = Exercise
fields = ["url", "id", "name", "description", "unit", "instances"] fields = ["url", "id", "name", "description", "unit", "instances"]
class RememberMeSerializer(serializers.HyperlinkedModelSerializer):
"""Serializer for an RememberMe. Hyperlinks are used for relationships by default.
Serialized fields: remember_me
Attributes:
remember_me: Value of cookie used for remember me functionality
"""
class Meta:
model = RememberMe
fields = ["remember_me"]
...@@ -46,6 +46,5 @@ urlpatterns = format_suffix_patterns( ...@@ -46,6 +46,5 @@ urlpatterns = format_suffix_patterns(
path("api/auth/", include("rest_framework.urls")), path("api/auth/", include("rest_framework.urls")),
path("api/token/", TokenObtainPairView.as_view(), name="token_obtain_pair"), path("api/token/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
path("api/token/refresh/", TokenRefreshView.as_view(), name="token_refresh"), path("api/token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
path("api/remember_me/", views.RememberMe.as_view(), name="remember_me"),
] ]
) )
"""Contains views for the workouts application. These are mostly class-based views. """Contains views for the workouts application. These are mostly class-based views.
""" """
import base64
import pickle
from collections import namedtuple
from django.core.exceptions import PermissionDenied
from django.core.signing import Signer
from django.db.models import Q from django.db.models import Q
from rest_framework import filters, generics, mixins, permissions from rest_framework import filters, generics, mixins, permissions
from rest_framework.decorators import api_view from rest_framework.decorators import api_view
from rest_framework.parsers import JSONParser from rest_framework.parsers import JSONParser
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.reverse import reverse from rest_framework.reverse import reverse
from rest_framework_simplejwt.tokens import RefreshToken
from workouts.mixins import CreateListModelMixin from workouts.mixins import CreateListModelMixin
from workouts.models import Exercise, ExerciseInstance, Workout, WorkoutFile from workouts.models import Exercise, ExerciseInstance, Workout, WorkoutFile
...@@ -22,8 +15,8 @@ from workouts.permissions import (IsCoachAndVisibleToCoach, ...@@ -22,8 +15,8 @@ from workouts.permissions import (IsCoachAndVisibleToCoach,
IsOwnerOfWorkout, IsPublic, IsReadOnly, IsOwnerOfWorkout, IsPublic, IsReadOnly,
IsWorkoutPublic) IsWorkoutPublic)
from workouts.serializers import (ExerciseInstanceSerializer, from workouts.serializers import (ExerciseInstanceSerializer,
ExerciseSerializer, RememberMeSerializer, ExerciseSerializer, WorkoutFileSerializer,
WorkoutFileSerializer, WorkoutSerializer) WorkoutSerializer)
@api_view(["GET"]) @api_view(["GET"])
...@@ -45,53 +38,6 @@ def api_root(request, format=None): ...@@ -45,53 +38,6 @@ def api_root(request, format=None):
) )
# Allow users to save a persistent session in their browser
class RememberMe(
mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView,
):
serializer_class = RememberMeSerializer
def get(self, request):
if request.user.is_authenticated == False:
raise PermissionDenied
else:
return Response({"remember_me": self.rememberme()})
def post(self, request):
cookie_object = namedtuple("Cookies", request.COOKIES.keys())(
*request.COOKIES.values()
)
user = self.get_user(cookie_object)
refresh = RefreshToken.for_user(user)
return Response(
{
"refresh": str(refresh),
"access": str(refresh.access_token),
}
)
def get_user(self, cookie_object):
decode = base64.b64decode(cookie_object.remember_me)
user, sign = pickle.loads(decode)
# Validate signature
if sign == self.sign_user(user):
return user
def rememberme(self):
creds = [self.request.user, self.sign_user(str(self.request.user))]
return base64.b64encode(pickle.dumps(creds))
def sign_user(self, username):
signer = Signer()
signed_user = signer.sign(username)
return signed_user
class WorkoutList( class WorkoutList(
mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView
): ):
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment