-
+
@@ -240,7 +368,7 @@
'''
# ---
-# name: TestLoginView.test_post[/][login_view_content]
+# name: TestLoginView.test_post[/-/][login_view_content]
'''
@@ -262,7 +390,7 @@
- Un lien de connexion vous a été envoyé à l'adresse test@server.com. Veuillez vérifier votre boîte de réception et cliquer sur le lien pour vous connecter.
+ Un lien de connexion vous a été envoyé à l'adresse samuel@jackson.com. Veuillez vérifier votre boîte de réception et cliquer sur le lien pour vous connecter.
@@ -272,7 +400,7 @@
'''
# ---
-# name: TestLoginView.test_post[/topics/][login_view_content]
+# name: TestLoginView.test_post[/topics/-/topics/][login_view_content]
'''
@@ -294,7 +422,7 @@
- Un lien de connexion vous a été envoyé à l'adresse test@server.com. Veuillez vérifier votre boîte de réception et cliquer sur le lien pour vous connecter.
+ Un lien de connexion vous a été envoyé à l'adresse samuel@jackson.com. Veuillez vérifier votre boîte de réception et cliquer sur le lien pour vous connecter.
@@ -304,7 +432,7 @@
'''
# ---
-# name: TestLoginView.test_post[None][login_view_content]
+# name: TestLoginView.test_post[http://www.unallowed_host.com-/][login_view_content]
'''
@@ -326,7 +454,7 @@
- Un lien de connexion vous a été envoyé à l'adresse test@server.com. Veuillez vérifier votre boîte de réception et cliquer sur le lien pour vous connecter.
+ Un lien de connexion vous a été envoyé à l'adresse samuel@jackson.com. Veuillez vérifier votre boîte de réception et cliquer sur le lien pour vous connecter.
@@ -336,18 +464,6 @@
'''
# ---
-# name: TestSendMagicLink.test_payload_when_sending_magic_link[/-/][send_magic_link_payload]
- '{"sender": {"name": "La Communaut\\u00e9", "email": "noreply@inclusion.beta.gouv.fr"}, "to": [{"email": "samuel@jackson.com"}], "params": {"display_name": "Samuel J.", "login_link": "LOGIN_LINK"}, "templateId": 31}'
-# ---
-# name: TestSendMagicLink.test_payload_when_sending_magic_link[/topics/-/][send_magic_link_payload]
- '{"sender": {"name": "La Communaut\\u00e9", "email": "noreply@inclusion.beta.gouv.fr"}, "to": [{"email": "samuel@jackson.com"}], "params": {"display_name": "Samuel J.", "login_link": "LOGIN_LINK"}, "templateId": 31}'
-# ---
-# name: TestSendMagicLink.test_payload_when_sending_magic_link[None-/][send_magic_link_payload]
- '{"sender": {"name": "La Communaut\\u00e9", "email": "noreply@inclusion.beta.gouv.fr"}, "to": [{"email": "samuel@jackson.com"}], "params": {"display_name": "Samuel J.", "login_link": "LOGIN_LINK"}, "templateId": 31}'
-# ---
-# name: TestSendMagicLink.test_payload_when_sending_magic_link[http://www.unallowed_host.com-/][send_magic_link_payload]
- '{"sender": {"name": "La Communaut\\u00e9", "email": "noreply@inclusion.beta.gouv.fr"}, "to": [{"email": "samuel@jackson.com"}], "params": {"display_name": "Samuel J.", "login_link": "LOGIN_LINK"}, "templateId": 31}'
-# ---
-# name: TestSendMagicLink.test_payload_when_sending_magic_link[send_magic_link_payload]
- '{"sender": {"name": "La Communaut\\u00e9", "email": "noreply@inclusion.beta.gouv.fr"}, "to": [{"email": "samuel@jackson.com"}], "params": {"display_name": "Samuel J.", "login_link": "LOGIN_LINK"}, "templateId": 31}'
+# name: TestSendMagicLink.test_send_magic_link[send_magic_link_payload]
+ '{"sender": {"name": "La Communaut\\u00e9", "email": "noreply@inclusion.beta.gouv.fr"}, "to": [{"email": "edith@martin.co"}], "params": {"display_name": "Edith M.", "login_link": "LOGIN_LINK"}, "templateId": 31}'
# ---
diff --git a/lacommunaute/forum_member/tests/tests_view.py b/lacommunaute/forum_member/tests/tests_view.py
index 0ed945306..12674b4ce 100644
--- a/lacommunaute/forum_member/tests/tests_view.py
+++ b/lacommunaute/forum_member/tests/tests_view.py
@@ -8,7 +8,7 @@
from lacommunaute.forum_member.factories import ForumProfileFactory
from lacommunaute.forum_member.models import ForumProfile
from lacommunaute.forum_member.shortcuts import get_forum_member_display_name
-from lacommunaute.forum_member.views import ForumProfileUpdateView
+from lacommunaute.forum_member.views import ForumProfileUpdateView, send_magic_link
from lacommunaute.users.enums import IdentityProvider
from lacommunaute.users.factories import UserFactory
from lacommunaute.users.models import User
@@ -17,14 +17,16 @@
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from django.contrib.auth.tokens import default_token_generator
-from lacommunaute.forum_member.views import send_magic_link
from django.conf import settings
import json
from lacommunaute.utils.urls import clean_next_url
+from unittest import mock
PermissionHandler = get_class("forum_permission.handler", "PermissionHandler")
assign_perm = get_class("forum_permission.shortcuts", "assign_perm")
+next_url_tuples = [("/", "/"), ("/topics/", "/topics/"), ("http://www.unallowed_host.com", "/")]
+
@pytest.fixture(name="mock_respx_post_to_sib_smtp_url")
def mock_respx_post_to_sib_smtp_url_fixture():
@@ -33,14 +35,53 @@ def mock_respx_post_to_sib_smtp_url_fixture():
yield
-@pytest.fixture(name="user_token")
-def user_token_fixture():
- user = UserFactory(first_name="Samuel", last_name="Jackson", email="samuel@jackson.com")
+@pytest.fixture(name="mock_token_generator")
+def mock_token_generator_fixture():
+ with mock.patch.object(default_token_generator, "make_token", return_value="fixed-token"):
+ yield default_token_generator
+
+
+def custom_user_token(user, fixed_token=False):
uidb64 = urlsafe_base64_encode(force_bytes(user.pk))
- token = default_token_generator.make_token(user)
+ if fixed_token:
+ token = "fixed-token"
+ else:
+ token = default_token_generator.make_token(user)
return user, uidb64, token
+@pytest.fixture(name="user")
+def user_fixture():
+ return UserFactory(
+ first_name="Samuel",
+ last_name="Jackson",
+ email="samuel@jackson.com",
+ identity_provider=IdentityProvider.PRO_CONNECT.value,
+ )
+
+
+@pytest.fixture(name="user_token")
+def user_token_fixture(user):
+ return custom_user_token(user)
+
+
+@pytest.fixture(name="fixed_user_token")
+def fixed_user_token_fixture(user):
+ return custom_user_token(user, fixed_token=True)
+
+
+def validate_magiclink_payload(payload_as_str, uidb64, token, expected):
+ url = f"{settings.COMMU_PROTOCOL}://{settings.COMMU_FQDN}" + reverse(
+ "members:login_with_link", kwargs={"uidb64": uidb64, "token": token}
+ )
+ payload = json.loads(payload_as_str)
+
+ if payload["params"]["login_link"] != url + "?" + urlencode({"next": expected}):
+ return False
+
+ return True
+
+
class ForumProfileUpdateViewTest(TestCase):
def test_success_url(self):
forum_profiles = ForumProfileFactory()
@@ -92,27 +133,21 @@ def test_show_linkedin_link(self, client, db):
class TestSendMagicLink:
- @pytest.mark.parametrize(
- "next_url,expected",
- [(None, "/"), ("/", "/"), ("/topics/", "/topics/"), ("http://www.unallowed_host.com", "/")],
- )
- def test_payload_when_sending_magic_link(
- self, client, db, user_token, next_url, expected, snapshot, mock_respx_post_to_sib_smtp_url
- ):
- user, uidb64, token = user_token
+ def test_send_magic_link(self, db, snapshot, mock_respx_post_to_sib_smtp_url):
+ user = UserFactory(first_name="Edith", last_name="Martin", email="edith@martin.co")
+ next_url = "/topics/"
+ send_magic_link(user, next_url)
+
+ token = default_token_generator.make_token(user)
+ uidb64 = urlsafe_base64_encode(force_bytes(user.pk))
url = reverse("members:login_with_link", kwargs={"uidb64": uidb64, "token": token})
query_params = urlencode({"next": clean_next_url(next_url)})
login_link = f"{settings.COMMU_PROTOCOL}://{settings.COMMU_FQDN}{url}?{query_params}"
- send_magic_link(user, next_url)
-
- payload = json.loads(respx.calls[0].request.content.decode())
- assert payload["params"][
- "login_link"
- ] == f"{settings.COMMU_PROTOCOL}://{settings.COMMU_FQDN}{url}?" + urlencode({"next": expected})
- assert respx.calls[0].request.content.decode().replace(login_link, "LOGIN_LINK") == snapshot(
- name="send_magic_link_payload"
- )
+ payload_as_str = respx.calls[0].request.content.decode()
+ payload = json.loads(payload_as_str)
+ assert payload["params"]["login_link"] == login_link
+ assert payload_as_str.replace(login_link, "LOGIN_LINK") == snapshot(name="send_magic_link_payload")
class TestLoginView:
@@ -124,39 +159,64 @@ def test_content(self, client, db, next_url, snapshot):
content = parse_response_to_soup(response, selector="main")
assert str(content) == snapshot(name="login_view_content")
- @pytest.mark.parametrize("next_url", [None, "/", "/topics/"])
- def test_post(self, client, db, next_url, snapshot, mock_respx_post_to_sib_smtp_url):
- user = UserFactory(email="test@server.com")
+ @pytest.mark.parametrize("next_url,expected", next_url_tuples)
+ def test_post(
+ self,
+ client,
+ db,
+ fixed_user_token,
+ next_url,
+ expected,
+ snapshot,
+ mock_token_generator,
+ mock_respx_post_to_sib_smtp_url,
+ ):
+ user, uidb64, token = fixed_user_token
response = client.post(
- reverse("members:login") + f"?next={next_url}",
- data={"email": user.email},
+ reverse("members:login"),
+ data={"email": user.email, "next": next_url},
)
+
assert response.status_code == 200
content = parse_response_to_soup(response, selector="main")
assert str(content) == snapshot(name="login_view_content")
- @pytest.mark.parametrize(
- "next_url,expected",
- [(None, "/"), ("/", "/"), ("/topics/", "/topics/"), ("http://www.unallowed_host.com", "/")],
- )
+ payload_as_str = respx.calls[0].request.content.decode()
+ assert validate_magiclink_payload(payload_as_str, uidb64, token, expected)
+
+ @pytest.mark.parametrize("next_url,expected", next_url_tuples)
def test_redirection_when_email_is_unknown(self, client, db, next_url, expected, mock_respx_post_to_sib_smtp_url):
response = client.post(
- reverse("members:login") + f"?next={next_url}" if next_url else reverse("members:login"),
- data={"email": "john@travolta.es"},
+ reverse("members:login"),
+ data={"email": "john@travolta.es", "next": next_url},
)
assert response.status_code == 302
assert response.url == reverse("members:create") + "?" + urlencode(
{"email": "john@travolta.es", "next": expected}
)
+ @pytest.mark.parametrize("next_url,expected", next_url_tuples)
+ def test_user_is_already_authenticated(self, client, db, next_url, expected):
+ user = UserFactory()
+ client.force_login(user)
+ url = reverse("members:login") + f"?next={next_url}"
+ response = client.get(url)
+ assert response.status_code == 302
+ assert response.url == expected
+
class TestCreateUserView:
- def test_post_new_email(self, client, db, snapshot, mock_respx_post_to_sib_smtp_url):
+ @pytest.mark.parametrize("next_url,expected", next_url_tuples)
+ def test_post_new_email(
+ self, client, db, next_url, expected, snapshot, mock_token_generator, mock_respx_post_to_sib_smtp_url
+ ):
email = "john@travolta.lt"
response = client.post(
- reverse("members:create"), data={"email": email, "first_name": "John", "last_name": "Travolta"}
+ reverse("members:create"),
+ data={"email": email, "first_name": "John", "last_name": "Travolta", "next": next_url},
)
assert response.status_code == 200
+
content = parse_response_to_soup(response, selector="main")
assert str(content) == snapshot(name="create_user_view_content")
@@ -168,15 +228,19 @@ def test_post_new_email(self, client, db, snapshot, mock_respx_post_to_sib_smtp_
created_forum_profile = ForumProfile.objects.get(user=created_user)
assert created_forum_profile.user.email == email
- def test_post_existing_email(self, client, db, snapshot, mock_respx_post_to_sib_smtp_url):
- first_name = "John"
- last_name = "Travolta"
- identity_provider = IdentityProvider.PRO_CONNECT.value
- user = UserFactory(
- email="john@travolta.it", first_name=first_name, last_name=last_name, identity_provider=identity_provider
- )
+ _, uidb64, token = custom_user_token(created_user, fixed_token=True)
+ payload_as_str = respx.calls[0].request.content.decode()
+ assert validate_magiclink_payload(payload_as_str, uidb64, token, expected)
+
+ @pytest.mark.parametrize("next_url,expected", next_url_tuples)
+ def test_post_existing_email(
+ self, client, db, user, next_url, expected, snapshot, mock_token_generator, mock_respx_post_to_sib_smtp_url
+ ):
+ first_name = user.first_name
+ last_name = user.last_name
response = client.post(
- reverse("members:create"), data={"email": user.email, "first_name": "Adam", "last_name": "Smith"}
+ reverse("members:create"),
+ data={"email": user.email, "first_name": "Adam", "last_name": "Smith", "next": next_url},
)
assert response.status_code == 200
content = parse_response_to_soup(response, selector="main")
@@ -187,6 +251,10 @@ def test_post_existing_email(self, client, db, snapshot, mock_respx_post_to_sib_
assert user.last_name == last_name
assert user.identity_provider == IdentityProvider.PRO_CONNECT
+ _, uidb64, token = custom_user_token(user, fixed_token=True)
+ payload_as_str = respx.calls[0].request.content.decode()
+ assert validate_magiclink_payload(payload_as_str, uidb64, token, expected)
+
class TestLoginWithLinkView:
def test_user_not_found(self, client, db):
@@ -197,7 +265,7 @@ def test_user_not_found(self, client, db):
assert response.url == reverse("members:login")
def test_invalid_token(self, client, db, user_token):
- user, uidb64, token = user_token
+ _, uidb64, __doc__ = user_token
response = client.get(reverse("members:login_with_link", kwargs={"uidb64": uidb64, "token": "invalid_token"}))
assert response.status_code == 302
assert response.url == reverse("members:login")
@@ -214,7 +282,7 @@ def test_expired_token(self, client, db, user_token):
assert response.url == reverse("members:login")
def test_success(self, client, db, user_token):
- user, uidb64, token = user_token
+ _, uidb64, token = user_token
next_url = "/topics/"
response = client.get(
@@ -225,7 +293,7 @@ def test_success(self, client, db, user_token):
assert client.session.get(IdentityProvider.MAGIC_LINK.name) == 1
def test_unallowed_next_url(self, client, db, user_token):
- user, uidb64, token = user_token
+ _, uidb64, token = user_token
next_url = "http://www.unallowed_host.com"
response = client.get(
diff --git a/lacommunaute/forum_member/views.py b/lacommunaute/forum_member/views.py
index 3b4fda4c1..a0e402af0 100644
--- a/lacommunaute/forum_member/views.py
+++ b/lacommunaute/forum_member/views.py
@@ -83,11 +83,11 @@ def dispatch(self, request, *args, **kwargs):
# if user uses a blocked email or a blocked domain
if request.user.is_authenticated:
next_url = request.GET.get("next", "/")
- return redirect(next_url)
+ return redirect(clean_next_url(next_url))
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
- next_url = self.request.GET.get("next", "/")
+ next_url = self.request.POST.get("next", "/")
email = form.cleaned_data["email"]
try:
diff --git a/lacommunaute/templates/registration/create_user.html b/lacommunaute/templates/registration/create_user.html
index c5618c914..d65408b44 100644
--- a/lacommunaute/templates/registration/create_user.html
+++ b/lacommunaute/templates/registration/create_user.html
@@ -38,7 +38,7 @@
{% include "partials/form_field.html" with field=form.first_name %}
{% include "partials/form_field.html" with field=form.last_name %}
-
+
diff --git a/lacommunaute/templates/registration/login_with_magic_link.html b/lacommunaute/templates/registration/login_with_magic_link.html
index 997a4920d..81655203a 100644
--- a/lacommunaute/templates/registration/login_with_magic_link.html
+++ b/lacommunaute/templates/registration/login_with_magic_link.html
@@ -36,6 +36,7 @@ {% trans "Login | Sign in" %}
{% endif %}
{% include "partials/form_field.html" with field=form.email %}
+