Skip to content

Commit

Permalink
Reduce complexity of User model
Browse files Browse the repository at this point in the history
  • Loading branch information
okeneo committed Sep 15, 2024
1 parent 7259768 commit e800b06
Show file tree
Hide file tree
Showing 26 changed files with 212 additions and 1,670 deletions.
110 changes: 36 additions & 74 deletions backend/account/admin.py
Original file line number Diff line number Diff line change
@@ -1,76 +1,38 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin

from .models import (
UserProfile,
VerificationEmailToken,
VerificationEmailUpdateToken,
VerificationPasswordResetToken,
)


@admin.register(UserProfile)
class UserProfileAdmin(UserAdmin):
model = UserProfile

add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("username", "password1", "password2", "email"),
},
),
)

list_display = (
"username",
"email",
"date_joined",
"last_login",
"pk",
)

fieldsets = UserAdmin.fieldsets + (
(
"Additional Information",
{
"fields": (
"role",
"bio",
"is_email_verified",
),
},
),
)


@admin.register(VerificationEmailToken)
class VerificationEmailTokenAdmin(admin.ModelAdmin):
model = VerificationEmailToken

list_display = (
"user",
"created_at",
)


@admin.register(VerificationEmailUpdateToken)
class VerificationEmailUpdateTokenAdmin(admin.ModelAdmin):
model = VerificationEmailUpdateToken

list_display = (
"user",
"created_at",
"new_email",
)


@admin.register(VerificationPasswordResetToken)
class VerificationPasswordResetTokenAdmin(admin.ModelAdmin):
model = VerificationPasswordResetToken

list_display = (
"user",
"created_at",
)
from django.contrib.auth.models import User


# @admin.register(User)
# class UserAdmin(UserAdmin):
# model = User

# add_fieldsets = (
# (
# None,
# {
# "classes": ("wide",),
# "fields": ("username", "password1", "password2", "email"),
# },
# ),
# )

# list_display = (
# "username",
# "email",
# "date_joined",
# "last_login",
# "pk",
# )

# fieldsets = UserAdmin.fieldsets + (
# (
# "Additional Information",
# {
# "fields": (
# "role",
# "bio",
# ),
# },
# ),
# )
30 changes: 0 additions & 30 deletions backend/account/backends.py

This file was deleted.

159 changes: 2 additions & 157 deletions backend/account/controller.py
Original file line number Diff line number Diff line change
@@ -1,163 +1,8 @@
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.validators import validate_email
from django.urls import reverse_lazy

from .models import (
UserProfile,
VerificationEmailToken,
VerificationEmailUpdateToken,
VerificationPasswordResetToken,
)
from .tasks import send_verification_email_task

EMAIL_TEMPLATES = {
"registration": {
"subject": "Verify your email address",
"message": "Follow this link to verify your account.",
"url_name": "blog:verify_email",
},
"update_email": {
"subject": "Email Update Verification",
"message": "If you didn't change it, you should click this link to recover it.",
"url_name": "blog:verify-email-update",
},
"reset_password": {
"subject": "Reset Password",
"message": "If you didn't change it, you should click this link to recover it.",
"url_name": "blog:verify-password-reset",
},
}


def send_verification_email(template_type, email, token_key):
"""Send a verification email based on the specified template.
Args:
template_type (str): Type of email template.
email (str): Recipient's email address.
token_key (str): Unique token key for email verification.
"""
template = EMAIL_TEMPLATES.get(template_type)
if not template:
raise ValueError(f"Unsupported email template: {template}.")

# Get email url.
url_name = template["url_name"]
url = reverse_lazy(url_name)
HOST = settings.HOST
email_url = f"{HOST}{url}?token_key={token_key}"

# Collect email subject and message.
template_message = template["message"]
message = f"{template_message}\n{email_url}"
subject = template["subject"]

# Pass email sending to celery.
send_verification_email_task.delay(subject, message, email)


def validate_email_token_key(token_key):
try:
token = VerificationEmailToken.objects.get(key=token_key)
except VerificationEmailToken.DoesNotExist:
raise ValidationError("Invalid verification token.")

if token.is_expired:
raise ValidationError("Token expired.")

return token


def validate_email_update_token_key(token_key):
try:
token = VerificationEmailUpdateToken.objects.get(key=token_key)
except VerificationEmailUpdateToken.DoesNotExist:
raise ValidationError("Invalid verification token.")

if token.is_expired:
raise ValidationError("Token expired.")

return token


def validate_reset_password_token_key(token_key):
try:
token = VerificationPasswordResetToken.objects.get(key=token_key)
except VerificationPasswordResetToken.DoesNotExist:
raise ValidationError("Invalid verification token.")

if token.is_expired:
raise ValidationError("Token expired.")

return token


def validate_resend_verification_email_operation(email):
"""Given an unverified email, validate that the user should be resent a new
verification email."""
if not email:
raise ValidationError("Email Required.")

try:
user = UserProfile.objects.get(email=email)
except UserProfile.DoesNotExist:
raise ValidationError("User not found.")

if user.is_active:
raise ValidationError("User already registered.")

try:
token = VerificationEmailToken.objects.get(user=user)
except VerificationEmailToken.DoesNotExist:
# A scenario where an inactive user tries to access this endpoint.
# This is equivalent to not user.is_active and not user.is_email_verified because
# there should always be a token for a user that is not active but are yet to
# verify their email.
raise ValidationError("Unauthorized access.")

if token.has_exceeded_maximum_attempts:
raise ValidationError("Exceeded maximum send attempts.")

return token, user


def validate_new_email(new_email, user):
MAX_LENGTH = 255

if not new_email:
raise ValidationError("New email required.")

new_email = clean_email(new_email)

if len(new_email) > MAX_LENGTH:
raise ValidationError("The new email address is too long.")

try:
validate_email(new_email)
except ValidationError as e:
raise ValidationError(str(e))

if user.email == new_email:
raise ValidationError(
"The new email address must be different from the current email address."
)

try:
UserProfile.objects.get(email=new_email)
raise ValidationError(f"The email address '{new_email}' is already registered.")
except UserProfile.DoesNotExist:
return new_email


def clean_email(email):
cleaned_email = email.lstrip("\n\r ").rstrip("\n\r ")
cleaned_email = cleaned_email.lower()
return cleaned_email
from django.contrib.auth.models import User


def get_sentinel_user():
return UserProfile.objects.get(username="deleted")
return User.objects.get(username="deleted")


def handle_deleted_user_comments(user):
Expand Down
3 changes: 1 addition & 2 deletions backend/account/management/commands/generate_tokens.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
from django.db import transaction
from rest_framework.authtoken.models import Token
Expand All @@ -21,7 +21,6 @@ def add_arguments(self, parser):
@transaction.atomic
def handle(self, *args, **options):
usernames = options["usernames"]
User = get_user_model()
if usernames:
users = User.objects.filter(username__in=usernames)
if not users:
Expand Down
Loading

0 comments on commit e800b06

Please sign in to comment.