Skip to content

Commit

Permalink
feat(api): introduce auth action to verify email for legacy accounts
Browse files Browse the repository at this point in the history
This commit can be reverted once all email addresses have been
verified.

Usage:
```python
from desecapi import models
for user in models.User.objects.filter(email_verified__isnull=True, is_active=True).all():
    user.send_confirmation_email('confirm-account', created=user.created)
```
  • Loading branch information
peterthomassen committed Jan 19, 2022
1 parent 9022c0e commit 9f07e2f
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 0 deletions.
24 changes: 24 additions & 0 deletions api/desecapi/migrations/0021_authenticatednoopuseraction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 4.0.1 on 2022-01-19 14:41

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('desecapi', '0020_user_email_verified'),
]

operations = [
migrations.CreateModel(
name='AuthenticatedNoopUserAction',
fields=[
('authenticateduseraction_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='desecapi.authenticateduseraction')),
],
options={
'managed': False,
},
bases=('desecapi.authenticateduseraction',),
),
]
10 changes: 10 additions & 0 deletions api/desecapi/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ def send_email(self, reason, context=None, recipient=None):
'activate-account': slow_lane,
'change-email': slow_lane,
'change-email-confirmation-old-email': fast_lane,
'confirm-account': slow_lane,
'password-change-confirmation': fast_lane,
'reset-password': fast_lane,
'delete-account': fast_lane,
Expand Down Expand Up @@ -1006,6 +1007,15 @@ def _act(self):
self.user.change_email(self.new_email)


class AuthenticatedNoopUserAction(AuthenticatedUserAction):

class Meta:
managed = False

def _act(self):
pass


class AuthenticatedResetPasswordUserAction(AuthenticatedUserAction):
new_password = models.CharField(max_length=128)

Expand Down
8 changes: 8 additions & 0 deletions api/desecapi/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
import re
from base64 import b64encode
from datetime import timedelta

import django.core.exceptions
from captcha.audio import AudioCaptcha
Expand Down Expand Up @@ -839,6 +840,13 @@ class Meta(AuthenticatedBasicUserActionSerializer.Meta):
fields = AuthenticatedBasicUserActionSerializer.Meta.fields + ('new_email',)


class AuthenticatedConfirmAccountUserActionSerializer(AuthenticatedBasicUserActionSerializer):
validity_period = timedelta(days=14)

class Meta(AuthenticatedBasicUserActionSerializer.Meta):
model = models.AuthenticatedNoopUserAction # confirmation happens during authentication, so nothing left to do


class AuthenticatedResetPasswordUserActionSerializer(AuthenticatedBasicUserActionSerializer):
new_password = serializers.CharField(write_only=True)

Expand Down
25 changes: 25 additions & 0 deletions api/desecapi/templates/emails/confirm-account/content.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Hi there,

You have registered with deSEC a long time ago (on {{ created|date:"Y-m-d" }}), probably to
set up a DNS domain (e.g. under dedyn.io). At the time when you registered
your account, we did not verify your email address.

Our terms now require that deSEC account holders provide an up-to-date contact
email address, in case we need to contact you (e.g. when there is a problem
with your domain). We are currently cleaning our database and making sure
that all addresses are verified.

Therefore, if you would like to continue using your deSEC account, you now
need to verify your email address. To do so, please use the following link
(valid for {% widthratio link_expiration_hours 24 1 %} days):

{{ confirmation_link.0 }}

If you do not want to continue using deSEC, we will delete your account once
the link has expired, without contacting you again. In case you miss the
deadline, we invite you to sign up again with the same email address.

We apologize for the slight inconvenience, and hope you enjoy deSEC!

Stay secure,
Nils
1 change: 1 addition & 0 deletions api/desecapi/templates/emails/confirm-account/subject.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[deSEC] Important: Please confirm your deSEC DNS account
1 change: 1 addition & 0 deletions api/desecapi/urls/version_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
# Authenticated Actions
path('v/activate-account/<code>/', views.AuthenticatedActivateUserActionView.as_view(), name='confirm-activate-account'),
path('v/change-email/<code>/', views.AuthenticatedChangeEmailUserActionView.as_view(), name='confirm-change-email'),
path('v/confirm-account/<code>/', views.AuthenticatedConfirmAccountUserAction.as_view(), name='confirm-confirm-account'),
path('v/reset-password/<code>/', views.AuthenticatedResetPasswordUserActionView.as_view(), name='confirm-reset-password'),
path('v/delete-account/<code>/', views.AuthenticatedDeleteUserActionView.as_view(), name='confirm-delete-account'),
path('v/renew-domain/<code>/', views.AuthenticatedRenewDomainBasicUserActionView.as_view(), name='confirm-renew-domain'),
Expand Down
9 changes: 9 additions & 0 deletions api/desecapi/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,15 @@ def post(self, request, *args, **kwargs):
})


class AuthenticatedConfirmAccountUserAction(AuthenticatedActionView):
html_url = '/confirm/confirm-account/{code}'
serializer_class = serializers.AuthenticatedConfirmAccountUserActionSerializer

def post(self, request, *args, **kwargs):
super().post(request, *args, **kwargs)
return Response({'detail': 'Success! Your account status has been confirmed.'})


class AuthenticatedResetPasswordUserActionView(AuthenticatedActionView):
html_url = '/confirm/reset-password/{code}/'
serializer_class = serializers.AuthenticatedResetPasswordUserActionSerializer
Expand Down

0 comments on commit 9f07e2f

Please sign in to comment.