From ae63861adb1d24c62479a202fb1617d0eb174d6a Mon Sep 17 00:00:00 2001 From: Disco DeDisco Date: Fri, 30 Jan 2026 21:33:30 -0500 Subject: [PATCH] migrations run for lyric models; authentication & views & their tests now account more fully for uuid over email as pk --- src/apps/lyric/authentication.py | 6 ++-- ..._token_id_alter_token_uid_alter_user_id.py | 28 +++++++++++++++++++ src/apps/lyric/models.py | 4 +-- src/apps/lyric/tests/test_authentication.py | 4 +-- src/apps/lyric/tests/test_views.py | 14 +++++++++- src/apps/lyric/views.py | 14 ++++++++-- 6 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 src/apps/lyric/migrations/0003_remove_token_id_alter_token_uid_alter_user_id.py diff --git a/src/apps/lyric/authentication.py b/src/apps/lyric/authentication.py index 237e6e2..5e48494 100644 --- a/src/apps/lyric/authentication.py +++ b/src/apps/lyric/authentication.py @@ -15,9 +15,9 @@ class PasswordlessAuthenticationBackend: except User.DoesNotExist: return User.objects.create(email=token.email) - def get_user(self, email): + def get_user(self, user_id): try: - return User.objects.get(email=email) + return User.objects.get(pk=user_id) except User.DoesNotExist: - return None # might also just pass (which = return None) + return None # could also pass (which = return None) \ No newline at end of file diff --git a/src/apps/lyric/migrations/0003_remove_token_id_alter_token_uid_alter_user_id.py b/src/apps/lyric/migrations/0003_remove_token_id_alter_token_uid_alter_user_id.py new file mode 100644 index 0000000..a94cfca --- /dev/null +++ b/src/apps/lyric/migrations/0003_remove_token_id_alter_token_uid_alter_user_id.py @@ -0,0 +1,28 @@ +# Generated by Django 6.0 on 2026-01-31 01:03 + +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lyric', '0002_token'), + ] + + operations = [ + migrations.RemoveField( + model_name='token', + name='id', + ), + migrations.AlterField( + model_name='token', + name='uid', + field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False), + ), + migrations.AlterField( + model_name='user', + name='id', + field=models.BigAutoField(primary_key=True, serialize=False), + ), + ] diff --git a/src/apps/lyric/models.py b/src/apps/lyric/models.py index 49cecb1..d766aed 100644 --- a/src/apps/lyric/models.py +++ b/src/apps/lyric/models.py @@ -7,10 +7,10 @@ class Token(models.Model): class User(models.Model): id = models.BigAutoField(primary_key=True) - # email = models.EmailField(primary_key=True) email = models.EmailField(unique=True) REQUIRED_FIELDS = [] USERNAME_FIELD = "email" - is_anonymous = False + is_authenticated = True + is_anonymous =False diff --git a/src/apps/lyric/tests/test_authentication.py b/src/apps/lyric/tests/test_authentication.py index ec9272e..7828269 100644 --- a/src/apps/lyric/tests/test_authentication.py +++ b/src/apps/lyric/tests/test_authentication.py @@ -47,10 +47,10 @@ class GetUserTest(TestCase): def test_gets_user_by_uuid(self): User.objects.create(email="fantaman@example.com") desired_user = User.objects.create(email="discoman@example.com") - found_user = PasswordlessAuthenticationBackend().get_user("discoman@example.com") + found_user = PasswordlessAuthenticationBackend().get_user(desired_user.id) self.assertEqual(found_user, desired_user) def test_returns_None_if_no_user_with_that_email(self): self.assertIsNone( - PasswordlessAuthenticationBackend().get_user("discoman@example.com") + PasswordlessAuthenticationBackend().get_user(999) ) diff --git a/src/apps/lyric/tests/test_views.py b/src/apps/lyric/tests/test_views.py index a3606b4..d361521 100644 --- a/src/apps/lyric/tests/test_views.py +++ b/src/apps/lyric/tests/test_views.py @@ -1,6 +1,7 @@ +from django.contrib import auth from django.test import TestCase from unittest import mock -from ..models import Token +from ..models import Token, User class SendLoginEmailViewTest(TestCase): def test_redirects_to_home_page(self): @@ -58,3 +59,14 @@ class LoginViewTest(TestCase): def test_redirects_to_home_page(self): response = self.client.get("/apps/lyric/login?token=abc123") self.assertRedirects(response, "/") + + def test_logs_in_if_given_valid_token(self): + anon_user = auth.get_user(self.client) + self.assertEqual(anon_user.is_authenticated, False) + + token = Token.objects.create(email="discoman@example.com") + self.client.get(f"/apps/lyric/login?token={token.uid}", follow=True) + + user = auth.get_user(self.client) + self.assertEqual(user.is_authenticated, True) + self.assertEqual(user.email, "discoman@example.com") diff --git a/src/apps/lyric/views.py b/src/apps/lyric/views.py index 23cc29a..a2607f9 100644 --- a/src/apps/lyric/views.py +++ b/src/apps/lyric/views.py @@ -1,8 +1,9 @@ -from django.contrib import messages +from django.contrib import auth, messages +from django.core.exceptions import ValidationError from django.core.mail import send_mail from django.shortcuts import redirect from django.urls import reverse -from .models import Token +from .models import Token, User from ..dashboard.forms import ItemForm def send_login_email(request): @@ -25,4 +26,13 @@ def send_login_email(request): return redirect("/") def login(request): + uid = request.GET.get("token") + if not uid: + return redirect("/") + + user = auth.authenticate(request, uid=uid) + if user is not None: + user.backend = "apps.lyric.authentication.PasswordlessAuthenticationBackend" + auth.login(request, user) + return redirect("/")