Skip to content

Commit

Permalink
[#2187] Implemented email-verification
Browse files Browse the repository at this point in the history
  • Loading branch information
Bart van der Schoor committed Mar 28, 2024
1 parent bd09926 commit 25d65f9
Show file tree
Hide file tree
Showing 18 changed files with 508 additions and 3 deletions.
5 changes: 4 additions & 1 deletion src/open_inwoner/accounts/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ class _UserAdmin(ImageCroppingMixin, UserAdmin):
"first_name",
)
fieldsets = (
(None, {"fields": ("uuid", "email", "password", "login_type")}),
(
None,
{"fields": ("uuid", "email", "verified_email", "password", "login_type")},
),
(
_("Personal info"),
{
Expand Down
21 changes: 20 additions & 1 deletion src/open_inwoner/accounts/middleware.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from django.urls import reverse_lazy

from open_inwoner.cms.utils.page_display import profile_page_is_published
from open_inwoner.configurations.models import SiteConfiguration
from open_inwoner.utils.middleware import BaseConditionalUserRedirectMiddleware


Expand All @@ -12,4 +14,21 @@ class NecessaryFieldsMiddleware(BaseConditionalUserRedirectMiddleware):

def requires_redirect(self, request) -> bool:
user = request.user
return user.require_necessary_fields() # and profile_page_is_published()
return user.require_necessary_fields() and profile_page_is_published()


class EmailVerificationMiddleware(BaseConditionalUserRedirectMiddleware):
"""
Redirect the user to a view to verify email
"""

redirect_url = reverse_lazy("profile:email_verification_user")
extra_pass_prefixes = (reverse_lazy("mail:verification"),)

def requires_redirect(self, request) -> bool:
user = request.user
return (
not user.has_verified_email()
and profile_page_is_published()
and SiteConfiguration.get_solo().email_verification_required
)
20 changes: 20 additions & 0 deletions src/open_inwoner/accounts/migrations/0075_user_verified_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 4.2.11 on 2024-03-28 15:25

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("accounts", "0074_merge_20240228_1544"),
]

operations = [
migrations.AddField(
model_name="user",
name="verified_email",
field=models.EmailField(
blank=True, default="", max_length=254, verbose_name="Verified email"
),
),
]
9 changes: 9 additions & 0 deletions src/open_inwoner/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(
verbose_name=_("Email address"),
)
verified_email = models.EmailField(
verbose_name=_("Verified email"),
blank=True,
default="",
)

def has_verified_email(self):
return self.verified_email != "" and self.email == self.verified_email

phonenumber = models.CharField(
verbose_name=_("Phonenumber"),
blank=True,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{% extends 'master.html' %}
{% load i18n static form_tags card_tags grid_tags solo_tags %}

{% block content %}
<div class="registration-grid">
{% render_grid %}
{% render_column span=9 %}
{% render_card tinted=True %}
{% get_solo 'configurations.SiteConfiguration' as config %}
<h1 class="h1">{% trans "E-mailadres bevestigen" %}</h1><br>
{% if config.email_verification_text %}<p class="p">{{ config.email_verification_text|urlize|linebreaksbr }}</p><br>{% endif %}
<form method="POST" id="email-verification-form" action="{{ request.get_full_path }}" class="form" novalidate>
{% csrf_token %}
{# {% for field in form.fields %}#}
{# {% autorender_field form field %}#}
{# {% endfor %}#}
{% trans "Verficatie email verzenden" as button_text %}
{% form_actions primary_icon='arrow_forward' primary_text=button_text %}
</form>
{% endrender_card %}
{% endrender_column %}
{% endrender_grid %}
</div>
{% endblock content %}
30 changes: 29 additions & 1 deletion src/open_inwoner/accounts/views/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.forms import Form
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.urls import NoReverseMatch, reverse
from django.utils.translation import gettext as _
from django.views.generic import UpdateView
from django.views.generic import FormView, UpdateView

from django_registration.backends.one_step.views import RegistrationView
from furl import furl
Expand All @@ -18,6 +19,7 @@
from open_inwoner.utils.hash import generate_email_from_string
from open_inwoner.utils.views import CommonPageMixin, LogMixin

from ...mail.verification import send_user_email_verification_mail
from ...utils.url import get_next_url_from
from ..forms import CustomRegistrationForm, NecessaryUserForm
from ..models import Invite, User
Expand Down Expand Up @@ -179,3 +181,29 @@ def get_initial(self):
initial["email"] = ""

return initial


class EmailVerificationUserView(LogMixin, LoginRequiredMixin, FormView):
model = User
# dummy form
form_class = Form
template_name = "accounts/email_verification.html"

def page_title(self):
return _("E-mailadres bevestigen")

def form_valid(self, form):
user = self.request.user

send_user_email_verification_mail(user)

messages.add_message(
self.request, messages.SUCCESS, _("Bevestigings e-mail is verzonden")
)

self.log_user_action(user, _("user requested e-mail address verification"))

return super().form_valid(form)

def get_success_url(self):
return get_next_url_from(self.request, default=reverse("pages-root"))
6 changes: 6 additions & 0 deletions src/open_inwoner/cms/profile/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
NewsletterSubscribeView,
)
from open_inwoner.accounts.views.actions import ActionDeleteView
from open_inwoner.accounts.views.registration import EmailVerificationUserView

app_name = "profile"

Expand Down Expand Up @@ -103,6 +104,11 @@
NecessaryFieldsUserView.as_view(),
name="registration_necessary",
),
path(
"register/email/verification/",
EmailVerificationUserView.as_view(),
name="email_verification_user",
),
path(
"newsletters",
NewsletterSubscribeView.as_view(),
Expand Down
1 change: 1 addition & 0 deletions src/open_inwoner/conf/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@
"open_inwoner.extended_sessions.middleware.SessionTimeoutMiddleware",
"open_inwoner.kvk.middleware.KvKLoginMiddleware",
"open_inwoner.accounts.middleware.NecessaryFieldsMiddleware",
"open_inwoner.accounts.middleware.EmailVerificationMiddleware",
"open_inwoner.cms.utils.middleware.AnonymousHomePageRedirectMiddleware",
"mozilla_django_oidc_db.middleware.SessionRefresh",
]
Expand Down
5 changes: 5 additions & 0 deletions src/open_inwoner/conf/parts/email_verification.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<p>Beste</p>
<p>Om door te gaan moet je je email-adres <a href="{{ verification_link }}">bevestigen</a><span class="color--secondary"> &rarr;</span></p>
<p>Mocht je geen behoefte hieraan hebben dan staat het je vrij om dit bericht te negeren </p>
<p>Met vriendelijke groet,
{{ site_name }} </p>
22 changes: 22 additions & 0 deletions src/open_inwoner/conf/parts/maileditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,28 @@ def _readfile(name):
},
],
},
"email_verification": {
"name": _("Email adress verification"),
"description": _("This email is used by users to verify their email address"),
"subject_default": _("Bevestig je email address voor {{ site_name }}"),
"body_default": _readfile("email_verification.html"),
"subject": [
{
"name": "site_name",
"description": _("Name of the site."),
},
],
"body": [
{
"name": "verification_link",
"description": _("Link to confirm email address"),
},
{
"name": "site_name",
"description": _("Name of the site"),
},
],
},
}
MAIL_EDITOR_BASE_CONTEXT = {"site_name": "Open Inwoner Platform"}
MAIL_EDITOR_DYNAMIC_CONTEXT = "open_inwoner.mail.context.mail_context"
1 change: 1 addition & 0 deletions src/open_inwoner/configurations/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ class SiteConfigurationAdmin(OrderedInlineModelAdminMixin, SingletonModelAdmin):
"email_new_message",
"recipients_email_digest",
"contact_phonenumber",
"email_verification_required",
)
},
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 4.2.11 on 2024-03-28 15:25

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("configurations", "0064_auto_20240304_1200"),
]

operations = [
migrations.AddField(
model_name="siteconfiguration",
name="email_verification_required",
field=models.BooleanField(
default=False,
help_text="Whether to require users to verify their email address",
verbose_name="Email verification required",
),
),
]
5 changes: 5 additions & 0 deletions src/open_inwoner/configurations/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,11 @@ class SiteConfiguration(SingletonModel):
blank=True,
default=list,
)
email_verification_required = models.BooleanField(
verbose_name=_("Email verification required"),
default=False,
help_text=_("Whether to require users to verify their email address"),
)

# contact info
contact_phonenumber = models.CharField(
Expand Down
Loading

0 comments on commit 25d65f9

Please sign in to comment.