Commit a79f92df authored by Samuel GAIST's avatar Samuel GAIST
Browse files

Merge branch '558_fix_url_prefix_usage' into 'django3_migration'

Fix url prefix usage in accounts/registration

See merge request !383
parents 1abc6723 a28a4610
Pipeline #43085 passed with stage
in 15 minutes and 15 seconds
......@@ -31,14 +31,20 @@ from datetime import timedelta
import simplejson as json
from django.conf import settings
from django.contrib.auth.models import User
from django.core import mail
from django.test import Client
from django.test import override_settings
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from ...utils.tests.helpers import reload_urlconf
from ..models import Profile
from ..models import SupervisionTrack
from ..models import TemporaryUrl
from ..models import generate_url_hash
from .core import AccountTestMixin
from .test_views import ViewTestCase
# ----------------------------------------------------------
......@@ -1402,3 +1408,77 @@ class TemporaryUrlTestCase(AccountTestCase):
.count()
)
self.assertEqual(count, 0)
# ----------------------------------------------------------
class EmailSendingTestCase(ViewTestCase, AccountTestMixin):
def run_email_check_yearly_revalidation_possible_rejection(self, prefix):
client = self.yearrevalidationuser
logged_in = self.client.login(username="yearrevalidationuser", password="1234")
self.assertTrue(logged_in)
self.assertEqual(client.username, "yearrevalidationuser")
revalidation_url = reverse("api_accounts:revalidate_account")
response = self.client.put(revalidation_url, format="json")
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.client.logout()
reference_url = response.wsgi_request.build_absolute_uri(
reverse(
"accounts:temp_url_rejection", kwargs={"hash_url": generate_url_hash()},
)
)
self.assertEqual(len(mail.outbox), 1)
text_lines = mail.outbox[0].body.split("\n")
generated_url = text_lines[8]
self.assertTrue(prefix in generated_url)
self.assertEqual(
generated_url.rsplit("/", 2)[0], reference_url.rsplit("/", 2)[0]
)
client = Client()
response = client.get(generated_url)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(mail.outbox), 2)
def test_email_for_yearly_revalidation_possible_rejection(self):
for prefix in ["", "/platform"]:
with self.subTest(url_prefix=prefix):
mail.outbox = []
# YearRevalidation user
# Create and assign key
now = datetime.datetime.now()
expiration_date_delta = datetime.timedelta(
days=settings.ACCOUNT_BLOCKAGE_AFTER_FIRST_REJECTION_DAYS
)
self.yearrevalidationuser.profile.supervision_key = (
self.yearrevalidationuser.profile._generate_current_supervision_key()
)
supervisiontrack = SupervisionTrack.objects.create(
supervisee=self.yearrevalidationuser,
supervisor=self.firstsupervisor,
is_valid=True,
start_date=now,
expiration_date=now + expiration_date_delta,
last_validation_date=now,
)
# Assign key to supervision track
supervisiontrack.supervision_key = (
self.yearrevalidationuser.profile.supervision_key
)
supervisiontrack.save()
self.yearrevalidationuser.profile.supervision.add(supervisiontrack)
self.yearrevalidationuser.profile.save()
self.yearrevalidationuser.save()
with override_settings(URL_PREFIX=prefix):
reload_urlconf()
self.run_email_check_yearly_revalidation_possible_rejection(prefix)
......@@ -26,7 +26,6 @@
###############################################################################
import datetime
from urllib.parse import urlparse
from django.conf import settings
from django.contrib import messages
......@@ -36,6 +35,7 @@ from django.contrib.auth.models import User
from django.db import models
from django.shortcuts import get_object_or_404
from django.shortcuts import render
from django.urls import reverse
from rest_framework.authtoken.models import Token
from ..utils import mail
......@@ -245,18 +245,22 @@ def perform_revalidation(request, supervisiontrack, supervisee, now):
# Inform supervisor about supervisee revalidation
# Possible supervisor rejection available
parsed_url = urlparse(settings.URL_PREFIX)
server_address = "%s://%s" % (parsed_url.scheme, parsed_url.hostname,)
temp_url_rejection = TemporaryUrl.objects.create_temporary_url(
TemporaryUrl.REJECTION, supervisiontrack
)
supervisor_rejection_url = request.build_absolute_uri(
reverse(
"accounts:temp_url_rejection",
kwargs={"hash_url": temp_url_rejection.url_hash},
)
)
context = {
"supervisor": supervisiontrack.supervisor,
"supervisee": supervisee,
"prefix": server_address,
"temp_url": temp_url_rejection.url_hash,
"supervisor_rejection_url": supervisor_rejection_url,
}
mail.send_email(
......@@ -293,13 +297,9 @@ def accept_supervisee(supervisiontrack, supervisee, now):
supervisee.is_active = True
supervisee.save()
parsed_url = urlparse(settings.URL_PREFIX)
server_address = "%s://%s" % (parsed_url.scheme, parsed_url.hostname)
context = {
"supervisor": supervisiontrack.supervisor,
"supervisee": supervisee,
"prefix": server_address,
}
mail.send_email(
......@@ -384,13 +384,9 @@ def load_temporary_url_rejection(request, hash_url):
supervisiontrack = temp_url.supervision_track
supervisee = supervisiontrack.supervisee
parsed_url = urlparse(settings.URL_PREFIX)
server_address = "%s://%s" % (parsed_url.scheme, parsed_url.hostname)
context = {
"supervisor": supervisiontrack.supervisor,
"supervisee": supervisee,
"prefix": server_address,
}
now = datetime.datetime.now()
......
......@@ -152,7 +152,7 @@ class RegistrationForm(forms.Form):
return self.cleaned_data
def save(self):
def save(self, request):
"""
Create the new ``User`` and ``RegistrationProfile``, and
returns the ``User`` (by calling
......@@ -161,6 +161,7 @@ class RegistrationForm(forms.Form):
"""
new_user = RegistrationProfile.objects.create_inactive_user(
request,
username=self.cleaned_data["username"],
first_name=self.cleaned_data["first_name"],
last_name=self.cleaned_data["last_name"],
......@@ -457,7 +458,7 @@ class RegistrationSupervisorForm(forms.Form):
return self.cleaned_data
def save(self):
def save(self, request):
"""
Create the new ``User`` and ``RegistrationProfile``, and
returns the ``User`` (by calling
......@@ -466,6 +467,7 @@ class RegistrationSupervisorForm(forms.Form):
"""
new_user = RegistrationProfile.objects.create_inactive_user(
request,
username=self.cleaned_data["username"],
first_name=self.cleaned_data["first_name"],
last_name=self.cleaned_data["last_name"],
......
......@@ -29,12 +29,12 @@ import datetime
import hashlib
import random
import re
from urllib.parse import urlparse
from django.conf import settings
from django.contrib.auth.models import User
from django.db import models
from django.db import transaction
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from ...accounts.models import Profile
......@@ -56,7 +56,7 @@ class RegistrationManager(models.Manager):
"""
def activate_user(self, activation_key):
def activate_user(self, activation_key, request):
"""
Validate an activation key and activate the corresponding
``User`` if valid.
......@@ -129,21 +129,23 @@ class RegistrationManager(models.Manager):
)
supervisor_user = supervisiontrack.supervisor
parsed_url = urlparse(settings.URL_PREFIX)
server_address = "%s://%s" % (
parsed_url.scheme,
parsed_url.hostname,
)
temp_url = TemporaryUrl.objects.create_temporary_url(
TemporaryUrl.VALIDATION, supervisiontrack
)
supervisor_validation_url = request.build_absolute_uri(
reverse(
"accounts:temp_url_validation",
kwargs={"hash_url": temp_url.url_hash},
)
)
contact_url = request.build_absolute_uri(reverse("contact"))
context = {
"supervisor": supervisor_user,
"supervisee": user,
"prefix": server_address,
"temp_url": temp_url.url_hash,
"supervisor_validation_url": supervisor_validation_url,
"contact_url": contact_url,
}
mail.send_email(
......@@ -159,7 +161,7 @@ class RegistrationManager(models.Manager):
return False
def create_inactive_user(
self, username, first_name, last_name, password, email, send_email=True
self, request, username, first_name, last_name, password, email, send_email=True
):
"""
Create a new, inactive ``User``, generate a
......@@ -209,14 +211,18 @@ class RegistrationManager(models.Manager):
registration_profile = self.create_profile(new_user)
if send_email:
parsed_url = urlparse(settings.URL_PREFIX)
server_address = "%s://%s" % (parsed_url.scheme, parsed_url.hostname)
registration_activation_url = request.build_absolute_uri(
reverse(
"registration-activation",
kwargs={"activation_key": registration_profile.activation_key},
)
)
contact_url = request.build_absolute_uri(reverse("contact"))
context = {
"activation_key": registration_profile.activation_key,
"expiration_days": settings.ACCOUNT_ACTIVATION_DAYS,
"user": new_user,
"prefix": server_address,
"registration_activation_url": registration_activation_url,
"contact_url": contact_url,
}
mail.send_email(
......
......@@ -4,12 +4,12 @@ Thank you for registering at the Idiap Research Institute's Biometric
Evaluation and Testing (BEAT) platform. Before we can activate your account
you must visit the following URL:
{{ prefix }}{% url 'registration-activation' activation_key %}
{{ registration_activation_url }}
If you don't do this in the next {{ expiration_days }} days, your registration
will be automatically cancelled and your data removed from our servers.
If you are having problems to activate your account, contact a member of our
support staff at {{ prefix }}{% url 'contact' %}.
support staff at {{ contact_url }}.
BEAT Administrators at the Idiap Research Institute
......@@ -9,6 +9,6 @@ You will be informed if your supervisor has accepted or rejected your request.
If no information is given by your supervisor, you will be allowed to make a new supervision request after a week.
If you are having problems to activate your account, contact a member of our
support staff at {{ prefix }}{% url 'contact' %}.
support staff at {{ contact_url }}.
BEAT Administrators at the Idiap Research Institute
......@@ -6,6 +6,6 @@ If he's still your current supervisee, you can drop this message.
However if he's not your supervisee anymore, you can immediately revoke your supervision by clicking on the following link:
{{ prefix }}/accounts/rejection/{{ temp_url }}
{{ supervisor_rejection_url }}
BEAT Administrators at the Idiap Research Institute
......@@ -12,9 +12,11 @@ Username: {{ supervisee.username }}
If you don't do this the supervisee will not be able to use his account and
will be deleted after 7 days.
You can also click on the following direct link to accomplish this action and accept this new supervisee at {{ prefix }}/accounts/validation/{{ temp_url }}
You can also click on the following direct link to accomplish this action and accept this new supervisee at:
{{ supervisor_validation_url }}
If you are having problems to activate your supervisee account, contact a member of our
support staff at {{ prefix }}{% url 'contact' %}.
support staff at {{ contact_url }}.
BEAT Administrators at the Idiap Research Institute
......@@ -12,10 +12,11 @@ Username: {{ supervisee.username }}
If you don't do this the supervisee will not be able to use his account in
future and will not be recognized as your supervisee.
You can also click on the following direct link to accomplish this action and accept this new supervisee
at {{ prefix }}/accounts/validation/{{ temp_url }}
You can also click on the following direct link to accomplish this action and accept this new supervisee at:
{{ supervisor_validation_url }}
If you are having problems to activate your supervisee account, contact a member of our
support staff at {{ prefix }}{% url 'contact' %}.
support staff at {{ contact_url }}.
BEAT Administrators at the Idiap Research Institute
# vim: set fileencoding=utf-8 :
###############################################################################
# #
# Copyright (c) 2020 Idiap Research Institute, http://www.idiap.ch/ #
# Contact: beat.support@idiap.ch #
# #
# This file is part of the beat.web module of the BEAT platform. #
# #
# Commercial License Usage #
# Licensees holding valid commercial BEAT licenses may use this file in #
# accordance with the terms contained in a written agreement between you #
# and Idiap. For further information contact tto@idiap.ch #
# #
# Alternatively, this file may be used under the terms of the GNU Affero #
# Public License version 3 as published by the Free Software and appearing #
# in the file LICENSE.AGPL included in the packaging of this file. #
# The BEAT platform is distributed in the hope that it will be useful, but #
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY #
# or FITNESS FOR A PARTICULAR PURPOSE. #
# #
# You should have received a copy of the GNU Affero Public License along #
# with the BEAT platform. If not, see http://www.gnu.org/licenses/. #
# #
###############################################################################
from django.contrib.auth.models import User
from django.core import mail
from django.test import Client
from django.test import override_settings
from django.urls import reverse
from ...accounts.models import generate_url_hash
from ...accounts.tests.core import AccountTestMixin
from ...accounts.tests.test_views import ViewTestCase
from ...utils.tests.helpers import reload_urlconf
# ----------------------------------------------------------
class EmailSendingTestCase(ViewTestCase, AccountTestMixin):
def run_email_check_signup(self, prefix):
client = Client()
response = client.post(
reverse("registration"),
dict(
username="test",
first_name="test",
last_name="test",
email="test_email@idiap.ch",
password1="123",
password2="123",
supervisor="firstsupervisor",
tos="on",
),
)
reference_url = response.wsgi_request.build_absolute_uri(
reverse("registration")
)
self.assertEqual(response.status_code, 302)
self.assertEqual(len(mail.outbox), 1)
text_lines = mail.outbox[0].body.split("\n")
generated_url = text_lines[6]
self.assertTrue(prefix in generated_url)
self.assertEqual(
generated_url.rsplit("/", 3)[0], reference_url.rsplit("/", 1)[0]
)
def test_email_for_signup(self):
for prefix in ["", "/platform"]:
with self.subTest(url_prefix=prefix):
mail.outbox = []
User.objects.filter(username="test").delete()
with override_settings(URL_PREFIX=prefix):
reload_urlconf()
self.run_email_check_signup(prefix)
def run_email_check_activation(self, prefix):
client = Client()
response = client.post(
reverse("registration"),
dict(
username="test",
first_name="test",
last_name="test",
email="test_email@idiap.ch",
password1="123",
password2="123",
supervisor="firstsupervisor",
tos="on",
),
)
self.assertEqual(response.status_code, 302)
reference_url = response.wsgi_request.build_absolute_uri(
reverse("registration")
)
self.assertEqual(len(mail.outbox), 1)
text_lines = mail.outbox[0].body.split("\n")
generated_url = text_lines[6]
self.assertTrue(prefix in generated_url)
self.assertEqual(
generated_url.rsplit("/", 3)[0], reference_url.rsplit("/", 1)[0]
)
response = client.get(generated_url)
self.assertEqual(len(mail.outbox), 2)
text_lines = mail.outbox[1].body.split("\n")
reference_url = response.wsgi_request.build_absolute_uri(
reverse(
"accounts:temp_url_validation",
kwargs={"hash_url": generate_url_hash()},
)
)
generated_url = text_lines[16]
self.assertTrue(prefix in generated_url)
self.assertEqual(
generated_url.rsplit("/", 2)[0], reference_url.rsplit("/", 2)[0]
)
def test_email_for_activation(self):
for prefix in ["", "/platform"]:
with self.subTest(url_prefix=prefix):
mail.outbox = []
User.objects.filter(username="test").delete()
with override_settings(URL_PREFIX=prefix):
reload_urlconf()
self.run_email_check_activation(prefix)
def run_email_check_reactivation(self, prefix):
client = Client()
response = client.post(
reverse("blocked_user_reactivation"),
dict( # nosec
username="blockeduser", password="1234", supervisor="firstsupervisor",
),
)
self.assertEqual(response.status_code, 200)
reference_url = response.wsgi_request.build_absolute_uri(
reverse(
"accounts:temp_url_validation",
kwargs={"hash_url": generate_url_hash()},
)
)
self.assertEqual(len(mail.outbox), 2)
text_lines = mail.outbox[0].body.split("\n")
generated_url = text_lines[16]
self.assertTrue(prefix in generated_url)
self.assertEqual(
generated_url.rsplit("/", 2)[0], reference_url.rsplit("/", 2)[0]
)
response = client.get(generated_url)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(mail.outbox), 3)
def test_email_for_reactivation(self):
for prefix in ["", "/platform"]:
with self.subTest(url_prefix=prefix):
mail.outbox = []
# Blocked user
self.blockeduser.profile.supervision_key = None
self.blockeduser.profile.rejection_date = None
self.blockeduser.is_active = False
self.blockeduser.profile.save()
self.blockeduser.save()
with override_settings(URL_PREFIX=prefix):
reload_urlconf()
self.run_email_check_reactivation(prefix)
......@@ -91,7 +91,7 @@ def activate(
"""
account = RegistrationProfile.objects.activate_user(activation_key)
account = RegistrationProfile.objects.activate_user(activation_key, request)
context = {"account": account, "expiration_days": settings.ACCOUNT_ACTIVATION_DAYS}
......@@ -174,12 +174,12 @@ def register(
# Check the form
if "supervisor" not in request.POST:
supervisor_form_active = True
form_supervisor = RegistrationSupervisorForm(
form = form_class()
form_supervisor = RegistrationFormTermsOfServiceSupervisor(
data=request.POST, files=request.FILES
)
form = form_class()
if form_supervisor.is_valid():
form_supervisor.save()
form_supervisor.save(request)
# success_url needs to be dynamically generated here; setting a
# a default value using reverse() will cause circular-import
# problems with the default URLConf for this application, which
......@@ -190,7 +190,7 @@ def register(
form = form_class(data=request.POST, files=request.FILES)
form_supervisor = RegistrationSupervisorForm()
if form.is_valid():
form.save()
form.save(request)
# success_url needs to be dynamically generated here; setting a
# a default value using reverse() will cause circular-import
# problems with the default URLConf for this application, which
......
......@@ -28,7 +28,6 @@
import datetime
import logging
from urllib.parse import urlparse
from django.conf import settings
from django.contrib import messages
......@@ -151,20 +150,24 @@ def blocked_user_reactivation(request):
supervisee.save()
# Inform by email the supervisor that he has a new supervisee request
parsed_url = urlparse(settings.URL_PREFIX)
server_address = "%s://%s" % (
parsed_url.scheme,
parsed_url.hostname,
)
temp_url = TemporaryUrl.objects.create_temporary_url(
TemporaryUrl.VALIDATION, supervisiontrack
)
supervisor_validation_url = request.build_absolute_uri(
reverse(
"accounts:temp_url_validation",
kwargs={"hash_url": temp_url.url_hash},
)
)
contact_url = request.build_absolute_uri(
reverse("contact")
)
context = {
"supervisor": supervisor,
"supervisee": supervisee,
"prefix": server_address,
"temp_url": temp_url.url_hash,
"supervisor_validation_url": supervisor_validation_url,
"contact_url": contact_url,
}