mirror of
https://github.com/ae-utbm/sith.git
synced 2026-04-27 09:06:02 +00:00
apply review comments
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
|
from django.contrib.messages import Message, get_messages
|
||||||
from django.db.models import Max
|
from django.db.models import Max
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
@@ -87,7 +88,15 @@ class TestThirdPartyAuth(TestCase):
|
|||||||
del self.query["signature"]
|
del self.query["signature"]
|
||||||
self.query["signature"] = hmac_hexdigest(new_key, self.query)
|
self.query["signature"] = hmac_hexdigest(new_key, self.query)
|
||||||
res = self.client.get(reverse("api-link:third-party-auth", query=self.query))
|
res = self.client.get(reverse("api-link:third-party-auth", query=self.query))
|
||||||
assert res.status_code == 403
|
assert list(get_messages(res.wsgi_request)) == [
|
||||||
|
Message(
|
||||||
|
level=40,
|
||||||
|
message=(
|
||||||
|
"La signature est incorrecte. "
|
||||||
|
"Nous ne pouvons pas garantir l'authenticité de la requête."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
def test_cgu_not_accepted(self):
|
def test_cgu_not_accepted(self):
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
@@ -102,13 +111,24 @@ class TestThirdPartyAuth(TestCase):
|
|||||||
assert res.status_code == 200
|
assert res.status_code == 200
|
||||||
|
|
||||||
def test_invalid_client(self):
|
def test_invalid_client(self):
|
||||||
|
self.client.force_login(self.user)
|
||||||
self.query["client_id"] = ApiClient.objects.aggregate(res=Max("id"))["res"] + 1
|
self.query["client_id"] = ApiClient.objects.aggregate(res=Max("id"))["res"] + 1
|
||||||
res = self.client.get(reverse("api-link:third-party-auth", query=self.query))
|
res = self.client.get(reverse("api-link:third-party-auth", query=self.query))
|
||||||
assert res.status_code == 403
|
assert list(get_messages(res.wsgi_request)) == [
|
||||||
|
Message(
|
||||||
|
level=40,
|
||||||
|
message="Les données fournies pour l'authentification sont incorrectes.",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
def test_missing_parameter(self):
|
def test_missing_parameter(self):
|
||||||
"""Test that a 403 is raised if there is a missing parameter."""
|
self.client.force_login(self.user)
|
||||||
del self.query["username"]
|
del self.query["username"]
|
||||||
self.query["signature"] = hmac_hexdigest(self.api_client.hmac_key, self.query)
|
self.query["signature"] = hmac_hexdigest(self.api_client.hmac_key, self.query)
|
||||||
res = self.client.get(reverse("api-link:third-party-auth", query=self.query))
|
res = self.client.get(reverse("api-link:third-party-auth", query=self.query))
|
||||||
assert res.status_code == 403
|
assert list(get_messages(res.wsgi_request)) == [
|
||||||
|
Message(
|
||||||
|
level=40,
|
||||||
|
message="Les données fournies pour l'authentification sont incorrectes.",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|||||||
+39
-12
@@ -3,10 +3,11 @@ from urllib.parse import unquote
|
|||||||
|
|
||||||
import pydantic
|
import pydantic
|
||||||
import requests
|
import requests
|
||||||
|
import sentry_sdk
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.shortcuts import render
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.views.generic import FormView, TemplateView
|
from django.views.generic import FormView, TemplateView
|
||||||
@@ -20,16 +21,19 @@ from core.schemas import UserProfileSchema
|
|||||||
from core.utils import hmac_hexdigest
|
from core.utils import hmac_hexdigest
|
||||||
|
|
||||||
|
|
||||||
class ThirdPartyAuthView(LoginRequiredMixin, FormView):
|
class ThirdPartyAuthView(AccessMixin, FormView):
|
||||||
form_class = ThirdPartyAuthForm
|
form_class = ThirdPartyAuthForm
|
||||||
template_name = "api/third_party/auth.jinja"
|
template_name = "api/third_party/auth.jinja"
|
||||||
success_url = reverse_lazy("core:index")
|
success_url = reverse_lazy("core:index")
|
||||||
|
|
||||||
def parse_params(self) -> ThirdPartyAuthParamsSchema:
|
def parse_params(self) -> ThirdPartyAuthParamsSchema | None:
|
||||||
"""Parse and check the authentication parameters.
|
"""Parse and check the authentication parameters.
|
||||||
|
|
||||||
Raises:
|
If parsing fails, messages will be created using the django message
|
||||||
PermissionDenied: if the verification failed.
|
infrastructure.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The parses parameters, or None if the parsing failed.
|
||||||
"""
|
"""
|
||||||
# This is here rather than in ThirdPartyAuthForm because
|
# This is here rather than in ThirdPartyAuthForm because
|
||||||
# the given parameters and their signature are checked during both
|
# the given parameters and their signature are checked during both
|
||||||
@@ -39,20 +43,39 @@ class ThirdPartyAuthView(LoginRequiredMixin, FormView):
|
|||||||
params = {key: unquote(val) for key, val in params.items()}
|
params = {key: unquote(val) for key, val in params.items()}
|
||||||
try:
|
try:
|
||||||
params = ThirdPartyAuthParamsSchema(**params)
|
params = ThirdPartyAuthParamsSchema(**params)
|
||||||
except pydantic.ValidationError as e:
|
except pydantic.ValidationError:
|
||||||
raise PermissionDenied("Wrong data format") from e
|
messages.error(
|
||||||
|
self.request, _("The data provided for authentication is incorrect")
|
||||||
|
)
|
||||||
|
return None
|
||||||
client: ApiClient = get_object_or_none(ApiClient, id=params.client_id)
|
client: ApiClient = get_object_or_none(ApiClient, id=params.client_id)
|
||||||
if not client:
|
if not client:
|
||||||
raise PermissionDenied
|
messages.error(
|
||||||
|
self.request, _("The data provided for authentication is incorrect")
|
||||||
|
)
|
||||||
|
return None
|
||||||
if not hmac.compare_digest(
|
if not hmac.compare_digest(
|
||||||
hmac_hexdigest(client.hmac_key, params.model_dump(exclude={"signature"})),
|
hmac_hexdigest(client.hmac_key, params.model_dump(exclude={"signature"})),
|
||||||
params.signature,
|
params.signature,
|
||||||
):
|
):
|
||||||
raise PermissionDenied("Bad signature")
|
messages.error(
|
||||||
|
self.request,
|
||||||
|
_(
|
||||||
|
"The signature is incorrect. "
|
||||||
|
"We cannot ensure the provenance of the request."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return None
|
||||||
return params
|
return params
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
if not request.user.is_authenticated:
|
||||||
|
return self.handle_no_permission()
|
||||||
self.params = self.parse_params()
|
self.params = self.parse_params()
|
||||||
|
if not self.params:
|
||||||
|
# if parameters parsing failed, shortcut the operation and display
|
||||||
|
# an empty page with just the error messages.
|
||||||
|
return render(request, "core/base.jinja")
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
@@ -73,10 +96,14 @@ class ThirdPartyAuthView(LoginRequiredMixin, FormView):
|
|||||||
client = ApiClient.objects.get(id=form.cleaned_data["client_id"])
|
client = ApiClient.objects.get(id=form.cleaned_data["client_id"])
|
||||||
user = UserProfileSchema.from_orm(self.request.user).model_dump()
|
user = UserProfileSchema.from_orm(self.request.user).model_dump()
|
||||||
data = {"user": user, "signature": hmac_hexdigest(client.hmac_key, user)}
|
data = {"user": user, "signature": hmac_hexdigest(client.hmac_key, user)}
|
||||||
response = requests.post(form.cleaned_data["callback_url"], json=data)
|
try:
|
||||||
|
ok = requests.post(form.cleaned_data["callback_url"], json=data).ok
|
||||||
|
except requests.RequestException as e:
|
||||||
|
sentry_sdk.capture_exception(e)
|
||||||
|
ok = False
|
||||||
self.success_url = reverse(
|
self.success_url = reverse(
|
||||||
"api-link:third-party-auth-result",
|
"api-link:third-party-auth-result",
|
||||||
kwargs={"result": "success" if response.ok else "failure"},
|
kwargs={"result": "success" if ok else "failure"},
|
||||||
)
|
)
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|||||||
@@ -148,14 +148,24 @@ msgid ""
|
|||||||
"href=\"%(sith_cgu_link)s\">the Students' Association</a> applies as soon as "
|
"href=\"%(sith_cgu_link)s\">the Students' Association</a> applies as soon as "
|
||||||
"the form is submitted."
|
"the form is submitted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Les politiques de confidentialité de <a href=\"%(privacy_link)s\">%(app)s</a> et de <a "
|
"Les politiques de confidentialité de <a href=\"%(privacy_link)s\">%(app)s</"
|
||||||
"href=\"%(sith_cgu_link)s\">l'Association des Etudiants</a> s'appliquent dès la soumission "
|
"a> et de <a href=\"%(sith_cgu_link)s\">l'Association des Etudiants</a> "
|
||||||
"du formulaire."
|
"s'appliquent dès la soumission du formulaire."
|
||||||
|
|
||||||
#: api/templates/api/third_party/auth.jinja
|
#: api/templates/api/third_party/auth.jinja
|
||||||
msgid "Confirmation of identity"
|
msgid "Confirmation of identity"
|
||||||
msgstr "Confirmation d'identité"
|
msgstr "Confirmation d'identité"
|
||||||
|
|
||||||
|
#: api/views.py
|
||||||
|
msgid "The data provided for authentication is incorrect"
|
||||||
|
msgstr "Les données fournies pour l'authentification sont incorrectes."
|
||||||
|
|
||||||
|
#: api/views.py
|
||||||
|
msgid ""
|
||||||
|
"The signature is incorrect. We cannot ensure the provenance of the request."
|
||||||
|
msgstr ""
|
||||||
|
"La signature est incorrecte. Nous ne pouvons pas garantir l'authenticité de la requête."
|
||||||
|
|
||||||
#: api/views.py
|
#: api/views.py
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid ""
|
msgid ""
|
||||||
@@ -167,7 +177,9 @@ msgstr ""
|
|||||||
|
|
||||||
#: api/views.py
|
#: api/views.py
|
||||||
msgid "You have been successfully authenticated. You can now close this page."
|
msgid "You have been successfully authenticated. You can now close this page."
|
||||||
msgstr "Vous avez été authentifié avec succès. Vous pouvez maintenant fermer cette page."
|
msgstr ""
|
||||||
|
"Vous avez été authentifié avec succès. Vous pouvez maintenant fermer cette "
|
||||||
|
"page."
|
||||||
|
|
||||||
#: api/views.py
|
#: api/views.py
|
||||||
msgid ""
|
msgid ""
|
||||||
@@ -175,9 +187,9 @@ msgid ""
|
|||||||
"during the interaction with the third-party application. Please contact the "
|
"during the interaction with the third-party application. Please contact the "
|
||||||
"managers of the latter."
|
"managers of the latter."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Votre authentification sur le site AE a fonctionné, mais une erreur est arrivée "
|
"Votre authentification sur le site AE a fonctionné, mais une erreur est "
|
||||||
"durant l'interaction avec l'application tierce. Veuillez contacter les responsables "
|
"arrivée durant l'interaction avec l'application tierce. Veuillez contacter "
|
||||||
"de cette dernière."
|
"les responsables de cette dernière."
|
||||||
|
|
||||||
#: club/forms.py
|
#: club/forms.py
|
||||||
msgid "Users to add"
|
msgid "Users to add"
|
||||||
@@ -263,6 +275,24 @@ msgstr "Vous devez être cotisant pour faire partie d'un club"
|
|||||||
msgid "You are already a member of this club"
|
msgid "You are already a member of this club"
|
||||||
msgstr "Vous êtes déjà membre de ce club."
|
msgstr "Vous êtes déjà membre de ce club."
|
||||||
|
|
||||||
|
#: club/forms.py
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Club state"
|
||||||
|
msgid "Club status"
|
||||||
|
msgstr "Etat du club"
|
||||||
|
|
||||||
|
#: club/forms.py
|
||||||
|
msgid "Active"
|
||||||
|
msgstr "Actif"
|
||||||
|
|
||||||
|
#: club/forms.py
|
||||||
|
msgid "Inactive"
|
||||||
|
msgstr "Inactif"
|
||||||
|
|
||||||
|
#: club/forms.py
|
||||||
|
msgid "All clubs"
|
||||||
|
msgstr "Tous les clubs"
|
||||||
|
|
||||||
#: club/models.py
|
#: club/models.py
|
||||||
msgid "slug name"
|
msgid "slug name"
|
||||||
msgstr "nom slug"
|
msgstr "nom slug"
|
||||||
@@ -383,37 +413,22 @@ msgstr "Cet email est déjà abonné à cette mailing"
|
|||||||
msgid "Unregistered user"
|
msgid "Unregistered user"
|
||||||
msgstr "Utilisateur non enregistré"
|
msgstr "Utilisateur non enregistré"
|
||||||
|
|
||||||
#: club/templates/club/club_list.jinja
|
|
||||||
msgid "Club list"
|
|
||||||
msgstr "Liste des clubs"
|
|
||||||
|
|
||||||
#: club/templates/club/club_list.jinja
|
#: club/templates/club/club_list.jinja
|
||||||
msgid "The list of all clubs existing at UTBM."
|
msgid "The list of all clubs existing at UTBM."
|
||||||
msgstr "La liste de tous les clubs existants à l'UTBM"
|
msgstr "La liste de tous les clubs existants à l'UTBM"
|
||||||
|
|
||||||
|
#: club/templates/club/club_list.jinja
|
||||||
|
msgid "Club list"
|
||||||
|
msgstr "Liste des clubs"
|
||||||
|
|
||||||
#: club/templates/club/club_list.jinja
|
#: club/templates/club/club_list.jinja
|
||||||
msgid "Filters"
|
msgid "Filters"
|
||||||
msgstr "Filtres"
|
msgstr "Filtres"
|
||||||
|
|
||||||
#: club/templates/club/club_list.jinja
|
#: club/templates/club/club_list.jinja core/templates/core/base/header.jinja
|
||||||
msgid "Name"
|
#: forum/templates/forum/macros.jinja matmat/templates/matmat/search_form.jinja
|
||||||
msgstr "Nom"
|
msgid "Search"
|
||||||
|
msgstr "Recherche"
|
||||||
#: club/templates/club/club_list.jinja
|
|
||||||
msgid "Club state"
|
|
||||||
msgstr "Etat du club"
|
|
||||||
|
|
||||||
#: club/templates/club/club_list.jinja
|
|
||||||
msgid "Active"
|
|
||||||
msgstr "Actif"
|
|
||||||
|
|
||||||
#: club/templates/club/club_list.jinja
|
|
||||||
msgid "Inactive"
|
|
||||||
msgstr "Inactif"
|
|
||||||
|
|
||||||
#: club/templates/club/club_list.jinja
|
|
||||||
msgid "All clubs"
|
|
||||||
msgstr "Tous les clubs"
|
|
||||||
|
|
||||||
#: club/templates/club/club_list.jinja core/templates/core/user_tools.jinja
|
#: club/templates/club/club_list.jinja core/templates/core/user_tools.jinja
|
||||||
msgid "New club"
|
msgid "New club"
|
||||||
@@ -1945,11 +1960,6 @@ msgstr "Connexion"
|
|||||||
msgid "Register"
|
msgid "Register"
|
||||||
msgstr "Inscription"
|
msgstr "Inscription"
|
||||||
|
|
||||||
#: core/templates/core/base/header.jinja forum/templates/forum/macros.jinja
|
|
||||||
#: matmat/templates/matmat/search_form.jinja
|
|
||||||
msgid "Search"
|
|
||||||
msgstr "Recherche"
|
|
||||||
|
|
||||||
#: core/templates/core/base/header.jinja
|
#: core/templates/core/base/header.jinja
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr "Déconnexion"
|
msgstr "Déconnexion"
|
||||||
@@ -4294,6 +4304,47 @@ msgstr ""
|
|||||||
msgid "this page"
|
msgid "this page"
|
||||||
msgstr "cette page"
|
msgstr "cette page"
|
||||||
|
|
||||||
|
#: eboutic/templates/eboutic/eboutic_main.jinja
|
||||||
|
msgid "Eurockéennes 2025 partnership"
|
||||||
|
msgstr "Partenariat Eurockéennes 2025"
|
||||||
|
|
||||||
|
#: eboutic/templates/eboutic/eboutic_main.jinja
|
||||||
|
msgid ""
|
||||||
|
"Our partner uses Weezevent to sell tickets. Weezevent may collect user info "
|
||||||
|
"according to its own privacy policy. By clicking the accept button you "
|
||||||
|
"consent to their terms of services."
|
||||||
|
msgstr ""
|
||||||
|
"Notre partenaire utilises Wezevent pour vendre ses billets. Weezevent peut "
|
||||||
|
"collecter des informations utilisateur conformément à sa propre politique de "
|
||||||
|
"confidentialité. En cliquant sur le bouton d'acceptation vous consentez à "
|
||||||
|
"leurs termes de service."
|
||||||
|
|
||||||
|
#: eboutic/templates/eboutic/eboutic_main.jinja
|
||||||
|
msgid "Privacy policy"
|
||||||
|
msgstr "Politique de confidentialité"
|
||||||
|
|
||||||
|
#: eboutic/templates/eboutic/eboutic_main.jinja
|
||||||
|
#: trombi/templates/trombi/comment_moderation.jinja
|
||||||
|
msgid "Accept"
|
||||||
|
msgstr "Accepter"
|
||||||
|
|
||||||
|
#: eboutic/templates/eboutic/eboutic_main.jinja
|
||||||
|
msgid ""
|
||||||
|
"You must be subscribed to benefit from the partnership with the Eurockéennes."
|
||||||
|
msgstr ""
|
||||||
|
"Vous devez être cotisant pour bénéficier du partenariat avec les "
|
||||||
|
"Eurockéennes."
|
||||||
|
|
||||||
|
#: eboutic/templates/eboutic/eboutic_main.jinja
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"This partnership offers a discount of up to 33%% on tickets for Friday, "
|
||||||
|
"Saturday and Sunday, as well as the 3-day package from Friday to Sunday."
|
||||||
|
msgstr ""
|
||||||
|
"Ce partenariat permet de profiter d'une réduction jusqu'à 33%% sur les "
|
||||||
|
"billets du vendredi, du samedi et du dimanche, ainsi qu'au forfait 3 jours, "
|
||||||
|
"du vendredi au dimanche."
|
||||||
|
|
||||||
#: eboutic/templates/eboutic/eboutic_main.jinja
|
#: eboutic/templates/eboutic/eboutic_main.jinja
|
||||||
msgid "There are no items available for sale"
|
msgid "There are no items available for sale"
|
||||||
msgstr "Aucun article n'est disponible à la vente"
|
msgstr "Aucun article n'est disponible à la vente"
|
||||||
@@ -5720,10 +5771,6 @@ msgstr "fin"
|
|||||||
msgid "Moderate Trombi comments"
|
msgid "Moderate Trombi comments"
|
||||||
msgstr "Modérer les commentaires du Trombi"
|
msgstr "Modérer les commentaires du Trombi"
|
||||||
|
|
||||||
#: trombi/templates/trombi/comment_moderation.jinja
|
|
||||||
msgid "Accept"
|
|
||||||
msgstr "Accepter"
|
|
||||||
|
|
||||||
#: trombi/templates/trombi/comment_moderation.jinja
|
#: trombi/templates/trombi/comment_moderation.jinja
|
||||||
msgid "Reject"
|
msgid "Reject"
|
||||||
msgstr "Refuser"
|
msgstr "Refuser"
|
||||||
@@ -5965,39 +6012,3 @@ msgstr "Vous ne pouvez plus écrire de commentaires, la date est passée."
|
|||||||
#, python-format
|
#, python-format
|
||||||
msgid "Maximum characters: %(max_length)s"
|
msgid "Maximum characters: %(max_length)s"
|
||||||
msgstr "Nombre de caractères max: %(max_length)s"
|
msgstr "Nombre de caractères max: %(max_length)s"
|
||||||
|
|
||||||
#: eboutic/templates/eboutic/eboutic_main.jinja
|
|
||||||
msgid "Eurockéennes 2025 partnership"
|
|
||||||
msgstr "Partenariat Eurockéennes 2025"
|
|
||||||
|
|
||||||
#: eboutic/templates/eboutic/eboutic_main.jinja
|
|
||||||
msgid ""
|
|
||||||
"Our partner uses Weezevent to sell tickets. Weezevent may collect user info "
|
|
||||||
"according to its own privacy policy. By clicking the accept button you "
|
|
||||||
"consent to their terms of services."
|
|
||||||
msgstr ""
|
|
||||||
"Notre partenaire utilises Wezevent pour vendre ses billets. Weezevent peut "
|
|
||||||
"collecter des informations utilisateur conformément à sa propre politique de "
|
|
||||||
"confidentialité. En cliquant sur le bouton d'acceptation vous consentez à "
|
|
||||||
"leurs termes de service."
|
|
||||||
|
|
||||||
#: eboutic/templates/eboutic/eboutic_main.jinja
|
|
||||||
msgid "Privacy policy"
|
|
||||||
msgstr "Politique de confidentialité"
|
|
||||||
|
|
||||||
#: eboutic/templates/eboutic/eboutic_main.jinja
|
|
||||||
msgid ""
|
|
||||||
"You must be subscribed to benefit from the partnership with the Eurockéennes."
|
|
||||||
msgstr ""
|
|
||||||
"Vous devez être cotisant pour bénéficier du partenariat avec les "
|
|
||||||
"Eurockéennes."
|
|
||||||
|
|
||||||
#: eboutic/templates/eboutic/eboutic_main.jinja
|
|
||||||
#, python-format
|
|
||||||
msgid ""
|
|
||||||
"This partnership offers a discount of up to 33%% on tickets for Friday, "
|
|
||||||
"Saturday and Sunday, as well as the 3-day package from Friday to Sunday."
|
|
||||||
msgstr ""
|
|
||||||
"Ce partenariat permet de profiter d'une réduction jusqu'à 33%% sur les "
|
|
||||||
"billets du vendredi, du samedi et du dimanche, ainsi qu'au forfait 3 jours, "
|
|
||||||
"du vendredi au dimanche."
|
|
||||||
|
|||||||
Reference in New Issue
Block a user