diff --git a/requirements.txt b/requirements.txt index b8c5a25b0..5a14473b8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ django-oauth-toolkit==1.0.0 django-cors-middleware==1.5.0 social-auth-core==2.0.0 social-auth-app-django==3.1.0 +six==1.16.0 django-extensions==2.2.6 channels==2.4 channels_redis==3.2.0 diff --git a/src/apps/profiles/tokens.py b/src/apps/profiles/tokens.py new file mode 100644 index 000000000..13439a6b0 --- /dev/null +++ b/src/apps/profiles/tokens.py @@ -0,0 +1,10 @@ +from django.contrib.auth.tokens import PasswordResetTokenGenerator +import six + + +class AccountActivationTokenGenerator(PasswordResetTokenGenerator): + def _make_hash_value(self, user, timestamp): + return (six.text_type(user.pk) + six.text_type(timestamp) + six.text_type(user.is_active)) + + +account_activation_token = AccountActivationTokenGenerator() diff --git a/src/apps/profiles/urls.py b/src/apps/profiles/urls.py index 085917008..f989ad405 100644 --- a/src/apps/profiles/urls.py +++ b/src/apps/profiles/urls.py @@ -6,6 +6,7 @@ urlpatterns = [ # url(r'^signup', views.sign_up, name="signup"), + path('activate//', views.activate, name='activate'), path('user//edit/', views.UserEditView.as_view(), name='user_edit'), path('user//notifications/', views.UserNotificationEdit.as_view(), name="user_notifications"), path('user//', views.UserDetailView.as_view(), name="user_profile"), diff --git a/src/apps/profiles/urls_accounts.py b/src/apps/profiles/urls_accounts.py index 7b956f9f7..6266d5216 100644 --- a/src/apps/profiles/urls_accounts.py +++ b/src/apps/profiles/urls_accounts.py @@ -5,7 +5,6 @@ app_name = "accounts" - urlpatterns = [ url(r'^signup', views.sign_up, name="signup"), # url(r'^user_profile', views.user_profile, name="user_profile"), diff --git a/src/apps/profiles/views.py b/src/apps/profiles/views.py index 581112b4f..93687968f 100644 --- a/src/apps/profiles/views.py +++ b/src/apps/profiles/views.py @@ -1,17 +1,24 @@ import json from django.conf import settings -from django.contrib.auth import authenticate, login +from django.contrib import messages +from django.contrib.auth import authenticate +from django.contrib.sites.shortcuts import get_current_site +from django.core.mail import EmailMessage from django.http import Http404 from django.shortcuts import render, redirect from django.contrib.auth import views as auth_views from django.contrib.auth.mixins import LoginRequiredMixin +from django.template.loader import render_to_string +from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode +from django.utils.encoding import force_bytes, force_str from django.views.generic import DetailView, TemplateView from api.serializers.profiles import UserSerializer, OrganizationDetailSerializer, OrganizationEditSerializer, \ UserNotificationSerializer from .forms import SignUpForm from .models import User, Organization, Membership +from .tokens import account_activation_token class LoginView(auth_views.LoginView): @@ -57,6 +64,44 @@ def get_context_data(self, **kwargs): return context +def activate(request, uidb64, token): + try: + # import pdb; pdb.set_trace(); + uid = force_str(urlsafe_base64_decode(uidb64)) + user = User.objects.get(pk=uid) + except User.DoesNotExist: + user = None + messages.error(request, f"User not found. Please sign up again.") + return redirect('accounts:signup') + if user is not None and account_activation_token.check_token(user, token): + user.is_active = True + user.save() + messages.success(request, f'Your account is fully setup! Please login.') + return redirect('accounts:login') + else: + user.delete() + messages.error(request, f"Activation link is invalid. Please sign up again.") + return redirect('accounts:signup') + return redirect('pages:home') + + +def activateEmail(request, user, to_email): + mail_subject = 'Activate your user account.' + message = render_to_string('profiles/emails/template_activate_account.html', { + 'user': user.username, + 'domain': get_current_site(request).domain, + 'uid': urlsafe_base64_encode(force_bytes(user.pk)), + 'token': account_activation_token.make_token(user), + 'protocol': 'https' if request.is_secure() else 'http' + }) + email = EmailMessage(mail_subject, message, to=[to_email]) + if email.send(): + messages.success(request, f'Dear {user.username}, please go to you email {to_email} inbox and click on \ + received activation link to confirm and complete the registration. *Note: Check your spam folder.') + else: + messages.error(request, f'Problem sending confirmation email to {to_email}, check if you typed it correctly.') + + def sign_up(request): context = {} context['chahub_signup_url'] = "{}/profiles/signup?next={}/social/login/chahub".format( @@ -70,7 +115,9 @@ def sign_up(request): username = form.cleaned_data.get('username') raw_password = form.cleaned_data.get('password1') user = authenticate(username=username, password=raw_password) - login(request, user, backend='django.contrib.auth.backends.ModelBackend') + user.is_active = False + user.save() + activateEmail(request, user, form.cleaned_data.get('email')) return redirect('pages:home') else: context['form'] = form diff --git a/src/templates/base.html b/src/templates/base.html index fc7221889..850123411 100644 --- a/src/templates/base.html +++ b/src/templates/base.html @@ -25,6 +25,37 @@ {% endblock %} + +{% if messages %} + {% for message in messages %} + {% if message.tags == 'success'%} + + {% elif message.tags == 'info' %} + + {% elif message.tags == 'warning' %} + + {% elif message.tags == 'error' %} + + {% endif %} + {% endfor %} +{% endif %} + +
diff --git a/src/templates/profiles/emails/template_activate_account.html b/src/templates/profiles/emails/template_activate_account.html new file mode 100644 index 000000000..98ee27122 --- /dev/null +++ b/src/templates/profiles/emails/template_activate_account.html @@ -0,0 +1,10 @@ +{% autoescape off %} +Hi {{ user.username }}, + +Please click on the link below to confirm your registration: + +{{ protocol }}://{{ domain }}{% url 'profiles:activate' uidb64=uid token=token %} + +This is an automatic message delivered by Codabench. + +{% endautoescape %}