Skip to content
Snippets Groups Projects
Commit 43711d79 authored by HeJ's avatar HeJ
Browse files

backoffice: rework registration to use CommunicationChannels

parent 2a9d0aa5
Branches
Tags
No related merge requests found
......@@ -3,13 +3,12 @@
Hallo {{ user.get_full_name|default:user.username }},
um deine Anmeldung bei C3 Assemblies zu bestätigen, klicke bitte folgenden Link:
https://{{ domain }}{% url 'signup_activate' uidb64=uid token=token %}
um deine Anmeldung zu bestätigen, klicke bitte folgenden Link:
https://{{ domain }}{% url 'backoffice:signup_activate' uidb64=uid channel_id=cid token=token %}
Falls du dich nicht angemeldet hast, ignoriere diese E-Mail bitte einfach.
Wir freuen uns auf deinen Besuch,
das C3 Assembly Team
Wir freuen uns auf deinen Besuch!
#####################################
......@@ -17,11 +16,10 @@ Wir freuen uns auf deinen Besuch,
Hi {{ user.get_full_name|default:user.username }},
in order to complete your registration with C3 Assemblies, please click the following link:
https://{{ domain }}{% url 'signup_activate' uidb64=uid token=token %}
in order to complete your registration, please click the following link:
https://{{ domain }}{% url 'backoffice:signup_activate' uidb64=uid channel_id=cid token=token %}
If you have not requested access or didn't expect this mail please simply ignore it.
Looking forward to having you at the event,
the C3 Assembly Team
Looking forward to having you at the event!
{% endautoescape %}
\ No newline at end of file
{% extends 'backoffice/base.html' %}
{% load i18n %}
{% block content %}
<div class="row mb-3">
<form class="mx-auto" action="{% url 'login' %}" method="POST">{% csrf_token %}
<div class="card">
<div class="card-body">
{{form.as_p}}
</div>
<div class="card-footer">
<input class="btn btn-primary float-right" type="submit" value="{% trans 'nav_login' %}">
</div>
</div>
</form>
</div>
<div class="row">
<div class="mx-auto">
<a class="btn btn-light" href="{% url 'signup' %}">{% trans 'registration_signup' %}</a>
<a class="btn btn-light" href="{% url 'password_reset' %}">{% trans 'registration_forgotpassword' %}</a>
</div>
</div>
{% endblock %}
\ No newline at end of file
......@@ -9,4 +9,10 @@ class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
return (str(user.pk) + str(timestamp) + str(user.profile.mail_verified))
class ChannelActivationTokenGenerator(PasswordResetTokenGenerator):
def _make_hash_value(self, channel, timestamp):
return (str(channel.user.pk) + str(timestamp) + str(channel.channel) + str(channel.address))
account_activation_token = AccountActivationTokenGenerator()
channel_activation_token = ChannelActivationTokenGenerator()
......@@ -10,7 +10,7 @@ urlpatterns = [
path('accounts/profile/', profile.profile, name='profile'),
path('accounts/signup/', auth.signup, name='signup'),
path('accounts/signup/done', auth.signup_done, name='account_activation_sent'),
re_path(r'^accounts/activate/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', auth.signup_activate, name='signup_activate'),
re_path(r'^accounts/activate/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<channel_id>\d+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', auth.signup_activate, name='signup_activate'),
path('login', auth.LoginView.as_view(), name='login'),
path('logout', auth.LogoutView.as_view(), name='logout'),
......
import logging
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import login, views as auth_views
from django.contrib.sites.shortcuts import get_current_site
......@@ -10,10 +11,10 @@ from django.urls import reverse, reverse_lazy
from django.utils.encoding import force_bytes, force_text
from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
from core.models import PlatformUser
from core.models import PlatformUser, UserCommunicationChannel
from ..forms import SignUpForm
from ..tokens import account_activation_token
from ..tokens import channel_activation_token
from .utils import extend_context, get_conference
......@@ -32,9 +33,6 @@ class LogoutView(auth_views.LogoutView):
def signup(request):
conference = get_conference(request)
if conference is None:
return redirect('conference_selection')
if request.method == 'POST':
form = SignUpForm(request.POST)
......@@ -43,24 +41,30 @@ def signup(request):
user.is_active = False
user.save()
channel = user.communication_channels.create(
channel=UserCommunicationChannel.Channel.MAIL,
address=form.cleaned_data['email'],
)
channel.save()
try:
subject = 'Activate Your RC3 Platform Account'
body = render_to_string('registration/account_activation_email.txt', {
'user': user,
'domain': get_current_site(request).domain,
'conference': conference,
'uid': urlsafe_base64_encode(force_bytes(user.pk)).decode('utf-8'),
'token': account_activation_token.make_token(user),
'cid': channel.id,
'token': channel_activation_token.make_token(channel),
})
message = EmailMessage(
to=[user.email],
subject=subject,
body=body,
reply_to=[conference.slug + '@c3assemblies.de'],
reply_to=settings.MAIL_REPLY_TO,
)
result = message.send()
logger.info('Sent account activation mail to "' + user.email + '" for ' + user.username + ' = ' + str(result))
return redirect('account_activation_sent')
return redirect('backoffice:account_activation_sent')
except Exception:
logger.exception('sending account activation mail failed')
user.delete()
......@@ -68,7 +72,7 @@ def signup(request):
else:
form = SignUpForm()
return render(request, 'registration/signup.html', extend_context(request, conference=conference, context={
return render(request, 'registration/signup.html', extend_context(request, conference=None, context={
'form': form,
}))
......@@ -76,32 +80,43 @@ def signup(request):
def signup_done(request):
conference = get_conference(request)
if conference is None:
return redirect('conference_selection')
return redirect('backoffice:conference_selection')
return render(request, 'registration/signup_done.html', extend_context(request, conference=conference, context={
'active_page': 'profile',
}))
def signup_activate(request, uidb64, token):
def signup_activate(request, uidb64, channel_id, token):
try:
uid = force_text(urlsafe_base64_decode(uidb64))
user = PlatformUser.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, PlatformUser.DoesNotExist):
user = None
channel = user.communication_channels.get(id=channel_id)
except Exception as err:
messages.warning(request, 'Invalid activation code.')
logger.warning(f'Account activation failed (user={user}, uidb64={uidb64}, channel={channel_id}, token={token}): {err}')
return redirect('backoffice:signup')
if user is not None and account_activation_token.check_token(user, token):
if not channel_activation_token.check_token(channel, token):
messages.warning(request, 'Invalid activation code.')
logger.warning(f'Account activation failed (user={user}, uidb64={uidb64}, channel={channel_id}, token={token}) due to invalid token.')
return redirect('backoffice:signup')
if channel.channel != UserCommunicationChannel.Channel.MAIL or user.email is None or user.email != channel.address:
messages.warning(request, 'Not an account registration.')
logger.warning(f'Account activation failed for non-registration-channel (user={user}, uidb64={uidb64}, channel={channel_id}, token={token})')
return redirect('backoffice:signup')
# first time registration: user.email was still set to the mail communication channel's address
user.is_active = True
user.profile.mail_verified = True
user.email = None
channel.is_verified = True
user.save()
channel.save()
login(request, user)
messages.success(request, 'Activation code OK')
logger.info('Account activated successfully: ' + user.username)
return redirect('index', request=request)
logger.info(f'Account activated successfully: {user.username}')
else:
messages.warning(request, 'Invalid activation code.')
logger.warning('Account activation failed (user={user}, uidb64={uidb64}, token={token})'.format(user=user, uidb64=uidb64, token=token))
return redirect('signup')
return redirect('backoffice:index', request=request)
......@@ -179,6 +179,7 @@ REST_FRAMEWORK = {
}
# Mail configuration
MAIL_REPLY_TO = []
SUPPORT_HTML_MAILS = False
# External Components Integration
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment