Confirm email views
This commit is contained in:
parent
247a7f7489
commit
5926224d7e
|
@ -36,9 +36,6 @@ FLOWER_PORT=8888
|
||||||
#FLOWER_USER=mouse
|
#FLOWER_USER=mouse
|
||||||
#FLOWER_PASSWORD=changeme
|
#FLOWER_PASSWORD=changeme
|
||||||
|
|
||||||
# make users confirm their email addresses after registration
|
|
||||||
CONFIRM_EMAIL=false
|
|
||||||
|
|
||||||
EMAIL_HOST="smtp.mailgun.org"
|
EMAIL_HOST="smtp.mailgun.org"
|
||||||
EMAIL_PORT=587
|
EMAIL_PORT=587
|
||||||
EMAIL_HOST_USER=mail@your.domain.here
|
EMAIL_HOST_USER=mail@your.domain.here
|
||||||
|
|
|
@ -36,9 +36,6 @@ FLOWER_PORT=8888
|
||||||
FLOWER_USER=mouse
|
FLOWER_USER=mouse
|
||||||
FLOWER_PASSWORD=changeme
|
FLOWER_PASSWORD=changeme
|
||||||
|
|
||||||
# make users confirm their email addresses after registration
|
|
||||||
CONFIRM_EMAIL=true
|
|
||||||
|
|
||||||
EMAIL_HOST="smtp.mailgun.org"
|
EMAIL_HOST="smtp.mailgun.org"
|
||||||
EMAIL_PORT=587
|
EMAIL_PORT=587
|
||||||
EMAIL_HOST_USER=mail@your.domain.here
|
EMAIL_HOST_USER=mail@your.domain.here
|
||||||
|
|
|
@ -30,6 +30,7 @@ def email_confirmation_email(user):
|
||||||
data["confirmation_link"] = user.confirmation_link
|
data["confirmation_link"] = user.confirmation_link
|
||||||
send_email.delay(user.email, *format_email("confirm_email", data))
|
send_email.delay(user.email, *format_email("confirm_email", data))
|
||||||
|
|
||||||
|
|
||||||
def invite_email(invite_request):
|
def invite_email(invite_request):
|
||||||
"""send out an invite code"""
|
"""send out an invite code"""
|
||||||
data = email_data()
|
data = email_data()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 3.2.4 on 2021-08-06 21:41
|
# Generated by Django 3.2.4 on 2021-08-06 23:24
|
||||||
|
|
||||||
import bookwyrm.models.base_model
|
import bookwyrm.models.base_model
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
@ -11,6 +11,11 @@ class Migration(migrations.Migration):
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="sitesettings",
|
||||||
|
name="require_confirm_email",
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name="user",
|
model_name="user",
|
||||||
name="confirmation_code",
|
name="confirmation_code",
|
|
@ -31,6 +31,7 @@ class SiteSettings(models.Model):
|
||||||
# registration
|
# registration
|
||||||
allow_registration = models.BooleanField(default=True)
|
allow_registration = models.BooleanField(default=True)
|
||||||
allow_invite_requests = models.BooleanField(default=True)
|
allow_invite_requests = models.BooleanField(default=True)
|
||||||
|
require_confirm_email = models.BooleanField(default=True)
|
||||||
|
|
||||||
# images
|
# images
|
||||||
logo = models.ImageField(upload_to="logos/", null=True, blank=True)
|
logo = models.ImageField(upload_to="logos/", null=True, blank=True)
|
||||||
|
|
|
@ -26,11 +26,13 @@ from .base_model import BookWyrmModel, DeactivationReason, new_access_code
|
||||||
from .federated_server import FederatedServer
|
from .federated_server import FederatedServer
|
||||||
from . import fields, Review
|
from . import fields, Review
|
||||||
|
|
||||||
|
|
||||||
def site_link():
|
def site_link():
|
||||||
"""helper for generating links to the site"""
|
"""helper for generating links to the site"""
|
||||||
protocol = "https" if USE_HTTPS else "http"
|
protocol = "https" if USE_HTTPS else "http"
|
||||||
return f"{protocol}://{DOMAIN}"
|
return f"{protocol}://{DOMAIN}"
|
||||||
|
|
||||||
|
|
||||||
class User(OrderedCollectionPageMixin, AbstractUser):
|
class User(OrderedCollectionPageMixin, AbstractUser):
|
||||||
"""a user who wants to read books"""
|
"""a user who wants to read books"""
|
||||||
|
|
||||||
|
@ -218,7 +220,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
||||||
self.following.order_by("-updated_date").all(),
|
self.following.order_by("-updated_date").all(),
|
||||||
remote_id=remote_id,
|
remote_id=remote_id,
|
||||||
id_only=True,
|
id_only=True,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
def to_followers_activity(self, **kwargs):
|
def to_followers_activity(self, **kwargs):
|
||||||
|
@ -228,7 +230,7 @@ class User(OrderedCollectionPageMixin, AbstractUser):
|
||||||
self.followers.order_by("-updated_date").all(),
|
self.followers.order_by("-updated_date").all(),
|
||||||
remote_id=remote_id,
|
remote_id=remote_id,
|
||||||
id_only=True,
|
id_only=True,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
def to_activity(self, **kwargs):
|
def to_activity(self, **kwargs):
|
||||||
|
|
|
@ -24,9 +24,6 @@ CELERY_ACCEPT_CONTENT = ["application/json"]
|
||||||
CELERY_TASK_SERIALIZER = "json"
|
CELERY_TASK_SERIALIZER = "json"
|
||||||
CELERY_RESULT_SERIALIZER = "json"
|
CELERY_RESULT_SERIALIZER = "json"
|
||||||
|
|
||||||
# make users confirm their email addresses after registration
|
|
||||||
CONFIRM_EMAIL = env("CONFIRM_EMAIL", True)
|
|
||||||
|
|
||||||
# email
|
# email
|
||||||
EMAIL_BACKEND = env("EMAIL_BACKEND", "django.core.mail.backends.smtp.EmailBackend")
|
EMAIL_BACKEND = env("EMAIL_BACKEND", "django.core.mail.backends.smtp.EmailBackend")
|
||||||
EMAIL_HOST = env("EMAIL_HOST")
|
EMAIL_HOST = env("EMAIL_HOST")
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{% extends "layout.html" %}
|
|
@ -46,7 +46,14 @@ urlpatterns = [
|
||||||
re_path("^api/updates/stream/(?P<stream>[a-z]+)/?$", views.get_unread_status_count),
|
re_path("^api/updates/stream/(?P<stream>[a-z]+)/?$", views.get_unread_status_count),
|
||||||
# authentication
|
# authentication
|
||||||
re_path(r"^login/?$", views.Login.as_view(), name="login"),
|
re_path(r"^login/?$", views.Login.as_view(), name="login"),
|
||||||
|
re_path(r"^login/(?P<confirmed>confirmed)?$", views.Login.as_view(), name="login"),
|
||||||
re_path(r"^register/?$", views.Register.as_view()),
|
re_path(r"^register/?$", views.Register.as_view()),
|
||||||
|
re_path(r"confirm-email/?$", views.ConfirmEmail.as_view(), name="confirm-email"),
|
||||||
|
re_path(
|
||||||
|
r"confirm-email/(?P<code>[A-Za-z0-9]+)/?$",
|
||||||
|
views.ConfirmEmailCode.as_view(),
|
||||||
|
name="confirm-email-code",
|
||||||
|
),
|
||||||
re_path(r"^logout/?$", views.Logout.as_view(), name="logout"),
|
re_path(r"^logout/?$", views.Logout.as_view(), name="logout"),
|
||||||
re_path(
|
re_path(
|
||||||
r"^password-reset/?$",
|
r"^password-reset/?$",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
""" make sure all our nice views are available """
|
""" make sure all our nice views are available """
|
||||||
from .announcements import Announcements, Announcement, delete_announcement
|
from .announcements import Announcements, Announcement, delete_announcement
|
||||||
from .authentication import Login, Register, Logout
|
from .authentication import Login, Register, Logout
|
||||||
|
from .authentication import ConfirmEmail, ConfirmEmailCode
|
||||||
from .author import Author, EditAuthor
|
from .author import Author, EditAuthor
|
||||||
from .block import Block, unblock
|
from .block import Block, unblock
|
||||||
from .books import Book, EditBook, ConfirmEditBook, Editions
|
from .books import Book, EditBook, ConfirmEditBook, Editions
|
||||||
|
|
|
@ -10,20 +10,21 @@ from django.views.decorators.csrf import csrf_exempt
|
||||||
from django.views import View
|
from django.views import View
|
||||||
|
|
||||||
from bookwyrm import emailing, forms, models
|
from bookwyrm import emailing, forms, models
|
||||||
from bookwyrm.settings import DOMAIN, CONFIRM_EMAIL
|
from bookwyrm.settings import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable= no-self-use
|
# pylint: disable=no-self-use
|
||||||
@method_decorator(csrf_exempt, name="dispatch")
|
@method_decorator(csrf_exempt, name="dispatch")
|
||||||
class Login(View):
|
class Login(View):
|
||||||
"""authenticate an existing user"""
|
"""authenticate an existing user"""
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request, confirmed=None):
|
||||||
"""login page"""
|
"""login page"""
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
# sene user to the login page
|
# send user to the login page
|
||||||
data = {
|
data = {
|
||||||
|
"show_confirmed_email": confirmed,
|
||||||
"login_form": forms.LoginForm(),
|
"login_form": forms.LoginForm(),
|
||||||
"register_form": forms.RegisterForm(),
|
"register_form": forms.RegisterForm(),
|
||||||
}
|
}
|
||||||
|
@ -37,14 +38,15 @@ class Login(View):
|
||||||
|
|
||||||
localname = login_form.data["localname"]
|
localname = login_form.data["localname"]
|
||||||
if "@" in localname: # looks like an email address to me
|
if "@" in localname: # looks like an email address to me
|
||||||
email = localname
|
|
||||||
try:
|
try:
|
||||||
username = models.User.objects.get(email=email)
|
username = models.User.objects.get(email=localname).username
|
||||||
except models.User.DoesNotExist: # maybe it's a full username?
|
except models.User.DoesNotExist: # maybe it's a full username?
|
||||||
username = localname
|
username = localname
|
||||||
else:
|
else:
|
||||||
username = "%s@%s" % (localname, DOMAIN)
|
username = "%s@%s" % (localname, DOMAIN)
|
||||||
password = login_form.data["password"]
|
password = login_form.data["password"]
|
||||||
|
|
||||||
|
# perform authentication
|
||||||
user = authenticate(request, username=username, password=password)
|
user = authenticate(request, username=username, password=password)
|
||||||
if user is not None:
|
if user is not None:
|
||||||
# successful login
|
# successful login
|
||||||
|
@ -53,6 +55,12 @@ class Login(View):
|
||||||
user.save(broadcast=False, update_fields=["last_active_date"])
|
user.save(broadcast=False, update_fields=["last_active_date"])
|
||||||
return redirect(request.GET.get("next", "/"))
|
return redirect(request.GET.get("next", "/"))
|
||||||
|
|
||||||
|
# maybe the user is pending email confirmation
|
||||||
|
if models.User.objects.filter(
|
||||||
|
username=username, is_active=False, deactivation_reason="pending"
|
||||||
|
).exists():
|
||||||
|
return redirect("confirm-email")
|
||||||
|
|
||||||
# login errors
|
# login errors
|
||||||
login_form.non_field_errors = "Username or password are incorrect"
|
login_form.non_field_errors = "Username or password are incorrect"
|
||||||
register_form = forms.RegisterForm()
|
register_form = forms.RegisterForm()
|
||||||
|
@ -60,12 +68,23 @@ class Login(View):
|
||||||
return TemplateResponse(request, "login.html", data)
|
return TemplateResponse(request, "login.html", data)
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
class Logout(View):
|
||||||
|
"""log out"""
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
"""done with this place! outa here!"""
|
||||||
|
logout(request)
|
||||||
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
class Register(View):
|
class Register(View):
|
||||||
"""register a user"""
|
"""register a user"""
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""join the server"""
|
"""join the server"""
|
||||||
if not models.SiteSettings.get().allow_registration:
|
settings = models.SiteSettings.get()
|
||||||
|
if not settings.allow_registration:
|
||||||
invite_code = request.POST.get("invite_code")
|
invite_code = request.POST.get("invite_code")
|
||||||
|
|
||||||
if not invite_code:
|
if not invite_code:
|
||||||
|
@ -109,14 +128,15 @@ class Register(View):
|
||||||
password,
|
password,
|
||||||
localname=localname,
|
localname=localname,
|
||||||
local=True,
|
local=True,
|
||||||
is_active=not CONFIRM_EMAIL,
|
deactivation_reason="pending" if settings.require_confirm_email else None,
|
||||||
|
is_active=not settings.require_confirm_email,
|
||||||
)
|
)
|
||||||
if invite:
|
if invite:
|
||||||
invite.times_used += 1
|
invite.times_used += 1
|
||||||
invite.invitees.add(user)
|
invite.invitees.add(user)
|
||||||
invite.save()
|
invite.save()
|
||||||
|
|
||||||
if CONFIRM_EMAIL:
|
if settings.require_confirm_email:
|
||||||
emailing.email_confirmation_email(user)
|
emailing.email_confirmation_email(user)
|
||||||
return redirect("confirm-email")
|
return redirect("confirm-email")
|
||||||
|
|
||||||
|
@ -124,11 +144,32 @@ class Register(View):
|
||||||
return redirect("get-started-profile")
|
return redirect("get-started-profile")
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name="dispatch")
|
class ConfirmEmail(View):
|
||||||
class Logout(View):
|
"""enter code to confirm email address"""
|
||||||
"""log out"""
|
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request): # pylint: disable=unused-argument
|
||||||
"""done with this place! outa here!"""
|
"""you need a code! keep looking"""
|
||||||
logout(request)
|
settings = models.SiteSettings.get()
|
||||||
return redirect("/")
|
if request.user.is_authenticated or not settings.require_confirm_email:
|
||||||
|
return redirect("/")
|
||||||
|
|
||||||
|
return TemplateResponse(request, "confirm_email.html")
|
||||||
|
|
||||||
|
|
||||||
|
class ConfirmEmailCode(View):
|
||||||
|
"""confirm email address"""
|
||||||
|
|
||||||
|
def get(self, request, code): # pylint: disable=unused-argument
|
||||||
|
"""you got the code! good work"""
|
||||||
|
settings = models.SiteSettings.get()
|
||||||
|
if request.user.is_authenticated or not settings.require_confirm_email:
|
||||||
|
return redirect("/")
|
||||||
|
|
||||||
|
# look up the user associated with this code
|
||||||
|
user = get_object_or_404(models.User, confirmation_code=code)
|
||||||
|
# update the user
|
||||||
|
user.is_active = True
|
||||||
|
user.deactivation_reason = None
|
||||||
|
user.save(broadcast=False, update_fields=["is_active", "deactivation_reason"])
|
||||||
|
# direct the user to log in
|
||||||
|
return redirect("login", confirmed="confirmed")
|
||||||
|
|
Loading…
Reference in New Issue