Skip to content

Commit

Permalink
chore: login in debug/dev mode (#829)
Browse files Browse the repository at this point in the history
## Description

🎸 lorsque le mode DEBUG est actif:
* afficher le lien magique de connexion au lieu d'utiliser les routes
`django.contrib.auth.urls`
* afficher une bannière rouge

## Type de changement

🎢 Nouvelle fonctionnalité (changement non cassant qui ajoute une
fonctionnalité).
🚧 technique

### Points d'attention

🦺 passage de la valeur de la variable `DEBUG` dans le
`context_processor`

### Captures d'écran (optionnel)

Banniere Debug Mode


![image](https://github.com/user-attachments/assets/bdeb154d-7ec3-4339-8017-727a313967dc)

Lien cliquable d'authentification en mode debug


![image](https://github.com/user-attachments/assets/cc8d16ad-941c-4578-84d2-eb1eb159a3f3)
  • Loading branch information
vincentporte authored Nov 20, 2024
1 parent 92bf7d8 commit 0a70301
Show file tree
Hide file tree
Showing 11 changed files with 78 additions and 30 deletions.
4 changes: 4 additions & 0 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from dotenv import load_dotenv
from machina import MACHINA_MAIN_STATIC_DIR, MACHINA_MAIN_TEMPLATE_DIR

from lacommunaute.utils.enums import Environment


load_dotenv()

Expand All @@ -17,6 +19,8 @@
SECRET_KEY = os.getenv("DJANGO_SECRET_KEY")

DEBUG = os.getenv("DJANGO_DEBUG", "False") == "True"
ENVIRONMENT = Environment.PROD

PARKING_PAGE = os.getenv("PARKING_PAGE", "False") == "True"

ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "communaute.inclusion.beta.gouv.fr,").split(",")
Expand Down
3 changes: 3 additions & 0 deletions config/settings/dev.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Enable django-debug-toolbar with Docker.
import socket

from lacommunaute.utils.enums import Environment

from .base import * # pylint: disable=wildcard-import,unused-wildcard-import # noqa: F403 F401


Expand All @@ -10,6 +12,7 @@


DEBUG = True
ENVIRONMENT = Environment.DEV

ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1", "192.168.0.1"]

Expand Down
3 changes: 0 additions & 3 deletions config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@
path("tracking/", include(tracking_urlpatterns_factory.urlpatterns)),
]

if settings.DEBUG is True:
urlpatterns += [path("", include("django.contrib.auth.urls"))]

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

urlpatterns += [
Expand Down
1 change: 1 addition & 0 deletions lacommunaute/templates/layouts/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
{% include "partials/header.html" %}
{% endblock %}
{% block sub_header %}{% endblock %}
{% if ENVIRONMENT != "PROD" %}<div id="debug-mode-banner" class="bg-danger text-white mt-3">DEV MODE</div>{% endif %}
<main id="main" role="main" class="s-main">
{% block messages %}
{% if messages %}
Expand Down
7 changes: 5 additions & 2 deletions lacommunaute/users/tests/__snapshots__/tests_views.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,9 @@
</main>
'''
# ---
# name: TestSendMagicLink.test_send_magic_link[send_magic_link_payload]
'{"sender": {"name": "La Communaut\\u00e9", "email": "[email protected]"}, "to": [{"email": "[email protected]"}], "params": {"display_name": "Edith M.", "login_link": "LOGIN_LINK"}, "templateId": 31}'
# name: TestSendMagicLink.test_send_magic_link[DEV-1][send_magic_link_payload]
'{"sender": {"name": "La Communaut\\u00e9", "email": "[email protected]"}, "to": [{"email": "[email protected]"}], "params": {"display_name": "Samuel J.", "login_link": "LOGIN_LINK"}, "templateId": 31}'
# ---
# name: TestSendMagicLink.test_send_magic_link[PROD-0][send_magic_link_payload]
'{"sender": {"name": "La Communaut\\u00e9", "email": "[email protected]"}, "to": [{"email": "[email protected]"}], "params": {"display_name": "Samuel J.", "login_link": "LOGIN_LINK"}, "templateId": 31}'
# ---
48 changes: 33 additions & 15 deletions lacommunaute/users/tests/tests_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
import respx
from django.conf import settings
from django.contrib.auth.tokens import default_token_generator
from django.contrib.messages.api import get_messages
from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.sessions.middleware import SessionMiddleware
from django.test import RequestFactory, override_settings
from django.urls import reverse
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
Expand All @@ -17,6 +21,7 @@
from lacommunaute.users.factories import UserFactory
from lacommunaute.users.models import User
from lacommunaute.users.views import send_magic_link
from lacommunaute.utils.enums import Environment
from lacommunaute.utils.testing import parse_response_to_soup
from lacommunaute.utils.urls import clean_next_url

Expand Down Expand Up @@ -79,21 +84,31 @@ def validate_magiclink_payload(payload_as_str, uidb64, token, expected):


class TestSendMagicLink:
def test_send_magic_link(self, db, snapshot, mock_respx_post_to_sib_smtp_url):
user = UserFactory(first_name="Edith", last_name="Martin", email="[email protected]")
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("users: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}"

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")
@pytest.mark.parametrize("env,count_msg", [(Environment.PROD, 0), (Environment.DEV, 1)])
def test_send_magic_link(self, db, user, snapshot, mock_respx_post_to_sib_smtp_url, env, count_msg):
with override_settings(ENVIRONMENT=env):
next_url = "/topics/"
request = RequestFactory().get("/")
SessionMiddleware(lambda request: None).process_request(request)
MessageMiddleware(lambda request: None).process_request(request)
send_magic_link(request, user, next_url)

token = default_token_generator.make_token(user)
uidb64 = urlsafe_base64_encode(force_bytes(user.pk))
url = reverse("users: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}"

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")

# we want messages do not appear in the productive environment
msgs = get_messages(request)
assert len(msgs._queued_messages) == count_msg
if msgs._queued_messages:
assert str(msgs._queued_messages[0]) == f'<a href="{login_link}">{login_link}</a> sent to {user.email}'


class TestLoginView:
Expand All @@ -105,6 +120,7 @@ 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")

@override_settings(ENVIRONMENT=Environment.PROD)
@pytest.mark.parametrize("next_url,expected", next_url_tuples)
def test_post(
self,
Expand Down Expand Up @@ -152,6 +168,7 @@ def test_user_is_already_authenticated(self, client, db, next_url, expected):


class TestCreateUserView:
@override_settings(ENVIRONMENT=Environment.PROD)
@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
Expand All @@ -178,6 +195,7 @@ def test_post_new_email(
payload_as_str = respx.calls[0].request.content.decode()
assert validate_magiclink_payload(payload_as_str, uidb64, token, expected)

@override_settings(ENVIRONMENT=Environment.PROD)
@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
Expand Down
12 changes: 9 additions & 3 deletions lacommunaute/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
from urllib.parse import urlencode

from django.conf import settings
from django.contrib import messages
from django.contrib.auth import login, logout
from django.contrib.auth.tokens import default_token_generator
from django.http import HttpResponseRedirect
from django.shortcuts import redirect, render
from django.urls import reverse
from django.utils.encoding import force_bytes, force_str
from django.utils.html import format_html
from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
from django.views.generic import FormView

Expand All @@ -24,7 +26,7 @@
logger = logging.getLogger(__name__)


def send_magic_link(user, next_url):
def send_magic_link(request, user, next_url):
token = default_token_generator.make_token(user)
uidb64 = urlsafe_base64_encode(force_bytes(user.pk))
url = reverse("users:login_with_link", kwargs={"uidb64": uidb64, "token": token})
Expand All @@ -38,6 +40,10 @@ def send_magic_link(user, next_url):
template_id=settings.SIB_MAGIC_LINK_TEMPLATE,
)

if settings.ENVIRONMENT == "DEV":
message = format_html('<a href="{0}">{0}</a> sent to {1}', login_link, user.email)
messages.success(request, message)


class LoginView(FormView):
template_name = "registration/login_with_magic_link.html"
Expand All @@ -62,7 +68,7 @@ def form_valid(self, form):
query_params = {"email": email, "next": clean_next_url(next_url)}
return HttpResponseRedirect(f"{base_url}?{urlencode(query_params)}")

send_magic_link(user, next_url)
send_magic_link(self.request, user, next_url)
return render(self.request, "registration/login_link_sent.html", {"email": email})


Expand Down Expand Up @@ -91,7 +97,7 @@ def form_valid(self, form):
)
ForumProfile.objects.create(user=user)

send_magic_link(user, next_url)
send_magic_link(self.request, user, next_url)
return render(self.request, "registration/login_link_sent.html", {"email": email})


Expand Down
5 changes: 5 additions & 0 deletions lacommunaute/utils/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@
class PeriodAggregation(models.TextChoices):
MONTH = "MONTH"
WEEK = "WEEK"


class Environment(models.TextChoices):
DEV = "DEV"
PROD = "PROD"
1 change: 1 addition & 0 deletions lacommunaute/utils/settings_context_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ def expose_settings(request):
"BASE_TEMPLATE": base_template,
"MATOMO_SITE_ID": settings.MATOMO_SITE_ID,
"MATOMO_BASE_URL": settings.MATOMO_BASE_URL,
"ENVIRONMENT": settings.ENVIRONMENT,
}
17 changes: 17 additions & 0 deletions lacommunaute/utils/tests/tests_middleware.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import pytest
from django.test import TestCase, override_settings

from lacommunaute.utils.enums import Environment


class ParkingMiddlewareTest(TestCase):
@override_settings(PARKING_PAGE=True)
Expand All @@ -13,3 +16,17 @@ def test_no_parking_page_middleware(self):
response = self.client.get("/")
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "pages/home.html")


class TestEnvironmentSettingsMiddleware:
@pytest.mark.parametrize(
"env,expected",
[
(Environment.PROD, False),
(Environment.DEV, True),
],
)
def test_prod_environment(self, client, db, env, expected):
with override_settings(ENVIRONMENT=env):
response = client.get("/")
assert ('id="debug-mode-banner"' in response.content.decode()) == expected
7 changes: 0 additions & 7 deletions lacommunaute/utils/tests/tests_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,8 @@ def _clear_url_caches():
sys.modules.pop("config.urls", None)


def test_django_urls_dev(settings):
settings.DEBUG = True
assert reverse("login") == "/login/"


def test_django_urls_prod(settings):
settings.DEBUG = False
with pytest.raises(NoReverseMatch):
reverse("login")
with pytest.raises(NoReverseMatch):
reverse("djdt:render_panel")

Expand Down

0 comments on commit 0a70301

Please sign in to comment.