diff options
author | Andrew Godwin | 2022-11-17 19:16:34 -0700 |
---|---|---|
committer | Andrew Godwin | 2022-11-17 19:16:34 -0700 |
commit | 6adfdbabe0d44c17f32abc9d48a6e252e2a0792e (patch) | |
tree | 6644c5eeab7970a9f9b8d9540b7ebe28cc499331 /users/models | |
parent | 2a3690d1c148da5dd799052403ba7290e1fb7de0 (diff) | |
download | takahe-6adfdbabe0d44c17f32abc9d48a6e252e2a0792e.tar.gz takahe-6adfdbabe0d44c17f32abc9d48a6e252e2a0792e.tar.bz2 takahe-6adfdbabe0d44c17f32abc9d48a6e252e2a0792e.zip |
Add signup and password reset
Diffstat (limited to 'users/models')
-rw-r--r-- | users/models/__init__.py | 1 | ||||
-rw-r--r-- | users/models/password_reset.py | 92 |
2 files changed, 93 insertions, 0 deletions
diff --git a/users/models/__init__.py b/users/models/__init__.py index 28d62b0..e46860e 100644 --- a/users/models/__init__.py +++ b/users/models/__init__.py @@ -3,5 +3,6 @@ from .domain import Domain # noqa from .follow import Follow, FollowStates # noqa from .identity import Identity, IdentityStates # noqa from .inbox_message import InboxMessage, InboxMessageStates # noqa +from .password_reset import PasswordReset # noqa from .user import User # noqa from .user_event import UserEvent # noqa diff --git a/users/models/password_reset.py b/users/models/password_reset.py new file mode 100644 index 0000000..90062d3 --- /dev/null +++ b/users/models/password_reset.py @@ -0,0 +1,92 @@ +import random +import string + +from asgiref.sync import sync_to_async +from django.conf import settings +from django.core.mail import send_mail +from django.db import models +from django.template.loader import render_to_string + +from core.models import Config +from stator.models import State, StateField, StateGraph, StatorModel + + +class PasswordResetStates(StateGraph): + new = State(try_interval=3) + sent = State() + + new.transitions_to(sent) + + @classmethod + async def handle_new(cls, instance: "PasswordReset"): + """ + Sends the password reset email. + """ + reset = await instance.afetch_full() + if reset.new_account: + await sync_to_async(send_mail)( + subject=f"{Config.system.site_name}: Confirm new account", + message=render_to_string( + "emails/new_account.txt", + { + "reset": reset, + "config": Config.system, + "settings": settings, + }, + ), + from_email=settings.EMAIL_FROM, + recipient_list=[reset.user.email], + ) + else: + await sync_to_async(send_mail)( + subject=f"{Config.system.site_name}: Reset password", + message=render_to_string( + "emails/password_reset.txt", + { + "reset": reset, + "config": Config.system, + "settings": settings, + }, + ), + from_email=settings.EMAIL_FROM, + recipient_list=[reset.user.email], + ) + return cls.sent + + +class PasswordReset(StatorModel): + """ + A password reset for a user (this is also how we create accounts) + """ + + state = StateField(PasswordResetStates) + + user = models.ForeignKey( + "users.user", + on_delete=models.CASCADE, + related_name="password_resets", + ) + + token = models.CharField(max_length=500, unique=True) + new_account = models.BooleanField() + + created = models.DateTimeField(auto_now_add=True) + updated = models.DateTimeField(auto_now=True) + + @classmethod + def create_for_user(cls, user): + return cls.objects.create( + user=user, + token="".join(random.choice(string.ascii_lowercase) for i in range(42)), + new_account=not user.password, + ) + + ### Async helpers ### + + async def afetch_full(self): + """ + Returns a version of the object with all relations pre-loaded + """ + return await PasswordReset.objects.select_related( + "user", + ).aget(pk=self.pk) |