feat: Securisation du Backend

This commit is contained in:
Luc SORIGNET
2026-02-27 10:45:36 +01:00
parent 2fef6d61a4
commit fa843097ba
55 changed files with 2898 additions and 910 deletions

View File

@ -0,0 +1,116 @@
"""
Tests de sécurité — Settings (SMTP)
Vérifie :
- Le mot de passe SMTP est absent des réponses GET (write_only)
- Authentification requise
"""
from django.test import TestCase, override_settings
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient
from rest_framework_simplejwt.tokens import RefreshToken
from Auth.models import Profile, ProfileRole
from Establishment.models import Establishment
from Settings.models import SMTPSettings
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def create_user_with_role(email):
user = Profile.objects.create_user(
username=email, email=email, password="TestPass!123"
)
est = Establishment.objects.create(
name=f"Ecole {email}", address="1 rue Test",
total_capacity=50, establishment_type=[1]
)
ProfileRole.objects.create(
profile=user, role_type=ProfileRole.RoleType.PROFIL_ADMIN,
establishment=est, is_active=True
)
return user, est
OVERRIDE = dict(
CACHES={'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}},
SESSION_ENGINE='django.contrib.sessions.backends.db',
)
# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
@override_settings(**OVERRIDE)
class SMTPSettingsAuthTest(TestCase):
"""Authentification requise sur l'endpoint SMTP."""
def setUp(self):
self.client = APIClient()
self.url = reverse('Settings:smtp_settings')
def test_sans_auth_retourne_401(self):
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
@override_settings(**OVERRIDE)
class SMTPPasswordNotExposedTest(TestCase):
"""
Le mot de passe SMTP ne doit jamais apparaître dans les réponses GET.
Avant la correction, smtp_password était retourné en clair à tout
utilisateur authentifié (incluant les parents).
"""
def setUp(self):
self.client = APIClient()
self.url = reverse('Settings:smtp_settings')
self.user, self.est = create_user_with_role('smtp_test@test.com')
SMTPSettings.objects.create(
establishment=self.est,
smtp_server='smtp.example.com',
smtp_port=587,
smtp_user='user@example.com',
smtp_password='super_secret_password_123',
use_tls=True,
)
def test_smtp_password_absent_de_la_reponse(self):
"""
GET /settings/smtp/ ne doit pas retourner smtp_password.
"""
token = str(RefreshToken.for_user(self.user).access_token)
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {token}')
response = self.client.get(self.url, {'establishment_id': self.est.id})
self.assertEqual(response.status_code, status.HTTP_200_OK)
data = response.json()
# Le mot de passe ne doit pas être dans la réponse (write_only)
self.assertNotIn(
'smtp_password', data,
"smtp_password ne doit pas être exposé dans les réponses API (OWASP A02 - Cryptographic Failures)"
)
# Vérification supplémentaire : la valeur secrète n'est pas dans la réponse brute
self.assertNotIn('super_secret_password_123', response.content.decode())
def test_smtp_password_accepte_en_ecriture(self):
"""
POST /settings/smtp/ doit accepter smtp_password (write_only ne bloque pas l'écriture).
"""
token = str(RefreshToken.for_user(self.user).access_token)
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {token}')
payload = {
'establishment': self.est.id,
'smtp_server': 'smtp.newserver.com',
'smtp_port': 465,
'smtp_user': 'new@example.com',
'smtp_password': 'nouveau_mot_de_passe',
'use_tls': False,
'use_ssl': True,
}
from rest_framework.test import APIRequestFactory
response = self.client.post(self.url, data=payload, format='json')
self.assertIn(response.status_code, [status.HTTP_200_OK, status.HTTP_201_CREATED])